summaryrefslogtreecommitdiff
path: root/usr/src/lib/libdiskmgt
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/lib/libdiskmgt
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libdiskmgt')
-rw-r--r--usr/src/lib/libdiskmgt/Makefile56
-rw-r--r--usr/src/lib/libdiskmgt/Makefile.com58
-rw-r--r--usr/src/lib/libdiskmgt/amd64/Makefile37
-rw-r--r--usr/src/lib/libdiskmgt/common/alias.c226
-rw-r--r--usr/src/lib/libdiskmgt/common/assoc_types.c80
-rw-r--r--usr/src/lib/libdiskmgt/common/bus.c250
-rw-r--r--usr/src/lib/libdiskmgt/common/cache.c991
-rw-r--r--usr/src/lib/libdiskmgt/common/controller.c309
-rw-r--r--usr/src/lib/libdiskmgt/common/disks_private.h273
-rw-r--r--usr/src/lib/libdiskmgt/common/drive.c1575
-rw-r--r--usr/src/lib/libdiskmgt/common/entry.c599
-rw-r--r--usr/src/lib/libdiskmgt/common/events.c496
-rw-r--r--usr/src/lib/libdiskmgt/common/findevs.c1885
-rw-r--r--usr/src/lib/libdiskmgt/common/inuse_dump.c96
-rw-r--r--usr/src/lib/libdiskmgt/common/inuse_fs.c344
-rw-r--r--usr/src/lib/libdiskmgt/common/inuse_lu.c343
-rw-r--r--usr/src/lib/libdiskmgt/common/inuse_mnt.c417
-rw-r--r--usr/src/lib/libdiskmgt/common/inuse_svm.c639
-rw-r--r--usr/src/lib/libdiskmgt/common/inuse_vxvm.c393
-rw-r--r--usr/src/lib/libdiskmgt/common/libdiskmgt.h240
-rw-r--r--usr/src/lib/libdiskmgt/common/llib-ldiskmgt33
-rw-r--r--usr/src/lib/libdiskmgt/common/media.c695
-rw-r--r--usr/src/lib/libdiskmgt/common/partition.c751
-rw-r--r--usr/src/lib/libdiskmgt/common/partition.h57
-rw-r--r--usr/src/lib/libdiskmgt/common/path.c330
-rw-r--r--usr/src/lib/libdiskmgt/common/slice.c1399
-rw-r--r--usr/src/lib/libdiskmgt/i386/Makefile32
-rw-r--r--usr/src/lib/libdiskmgt/sparc/Makefile32
-rw-r--r--usr/src/lib/libdiskmgt/sparcv9/Makefile38
-rw-r--r--usr/src/lib/libdiskmgt/spec/Makefile30
-rw-r--r--usr/src/lib/libdiskmgt/spec/Makefile.targ33
-rw-r--r--usr/src/lib/libdiskmgt/spec/amd64/Makefile42
-rw-r--r--usr/src/lib/libdiskmgt/spec/diskmgt.spec111
-rw-r--r--usr/src/lib/libdiskmgt/spec/i386/Makefile36
-rw-r--r--usr/src/lib/libdiskmgt/spec/sparc/Makefile36
-rw-r--r--usr/src/lib/libdiskmgt/spec/sparcv9/Makefile43
-rw-r--r--usr/src/lib/libdiskmgt/spec/versions39
37 files changed, 13044 insertions, 0 deletions
diff --git a/usr/src/lib/libdiskmgt/Makefile b/usr/src/lib/libdiskmgt/Makefile
new file mode 100644
index 0000000000..ad9854966a
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/Makefile
@@ -0,0 +1,56 @@
+#
+# 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 2002-2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.lib
+
+HDRS = libdiskmgt.h
+HDRDIR = common
+SUBDIRS = spec .WAIT $(MACH) $(BUILD64) $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all clean clobber install lint: $(SUBDIRS)
+
+install_h:
+
+check: $(CHECKHDRS)
+
+$(POFILE):
+
+$(MACH) $(MACH64) spec: FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
diff --git a/usr/src/lib/libdiskmgt/Makefile.com b/usr/src/lib/libdiskmgt/Makefile.com
new file mode 100644
index 0000000000..e376e7b5c3
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/Makefile.com
@@ -0,0 +1,58 @@
+#
+# 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"
+#
+
+LIBRARY = libdiskmgt.a
+VERS = .1
+OBJECTS = assoc_types.o \
+ entry.o cache.o drive.o controller.o alias.o path.o \
+ media.o slice.o partition.o findevs.o events.o \
+ bus.o inuse_mnt.o inuse_svm.o inuse_lu.o inuse_fs.o \
+ inuse_vxvm.o inuse_dump.o
+
+include ../../Makefile.lib
+
+LIBS = $(DYNLIB) $(LINTLIB)
+LDLIBS += -ldevinfo -ladm -ldevid -lkstat -lsysevent \
+ -lvolmgt -lnvpair -lefi -lc
+LDFLAGS += -R/opt/VRTSvxvm/lib
+$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC)
+
+SRCDIR = ../common
+MAPDIR = ../spec/$(TRANSMACH)
+SPECMAPFILE = $(MAPDIR)/mapfile
+
+CFLAGS += $(CCVERBOSE)
+CPPFLAGS += -D_REENTRANT -I$(SRC)/lib/libdiskmgt/common
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libdiskmgt/amd64/Makefile b/usr/src/lib/libdiskmgt/amd64/Makefile
new file mode 100644
index 0000000000..cb86d986a2
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/amd64/Makefile
@@ -0,0 +1,37 @@
+#
+# 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"
+#
+
+MAPDIR= ../spec/amd64
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libdiskmgt/common/alias.c b/usr/src/lib/libdiskmgt/common/alias.c
new file mode 100644
index 0000000000..4281db9288
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/alias.c
@@ -0,0 +1,226 @@
+/*
+ * 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 <fcntl.h>
+#include <libdevinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/dkio.h>
+#include <sys/sunddi.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+
+static int get_status(disk_t *diskp, int fd, nvlist_t *attrs);
+
+descriptor_t **
+alias_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type,
+ int *errp)
+{
+ switch (type) {
+ case DM_DRIVE:
+ return (drive_get_assocs(desc, errp));
+ }
+
+ *errp = EINVAL;
+ return (NULL);
+}
+
+nvlist_t *
+alias_get_attributes(descriptor_t *dp, int *errp)
+{
+ alias_t *ap;
+ nvlist_t *attrs = NULL;
+
+ /* Find the alias for this descriptor */
+
+ *errp = ENODEV;
+ for (ap = dp->p.disk->aliases; ap != NULL; ap = ap->next) {
+ if (libdiskmgt_str_eq(dp->name, ap->alias)) {
+ /* we found the alias for this descriptor */
+
+ if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ if (ap->target >= 0) {
+ if (nvlist_add_uint32(attrs, DM_LUN, ap->lun) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ if (nvlist_add_uint32(attrs, DM_TARGET, ap->target) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+ }
+
+ if (ap->wwn != NULL) {
+ if (nvlist_add_string(attrs, DM_WWN, ap->wwn) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+ }
+
+ if (ap->devpaths != NULL) {
+ /* get the status for this alias */
+ int fd;
+
+ fd = open(ap->devpaths->devpath, O_RDONLY|O_NDELAY);
+
+ if ((*errp = get_status(dp->p.disk, fd, attrs)) != 0) {
+ nvlist_free(attrs);
+ attrs = NULL;
+ }
+
+ if (fd >= 0) {
+ (void) close(fd);
+ }
+ }
+
+ *errp = 0;
+ break;
+ }
+ }
+
+ return (attrs);
+}
+
+descriptor_t *
+alias_get_descriptor_by_name(char *name, int *errp)
+{
+ descriptor_t **aliases;
+ int i;
+ descriptor_t *alias = NULL;
+
+ aliases = cache_get_descriptors(DM_ALIAS, errp);
+ if (*errp != 0) {
+ return (NULL);
+ }
+
+ for (i = 0; aliases[i]; i++) {
+ if (libdiskmgt_str_eq(name, aliases[i]->name)) {
+ alias = aliases[i];
+ } else {
+ /* clean up the unused descriptors */
+ cache_free_descriptor(aliases[i]);
+ }
+ }
+ free(aliases);
+
+ if (alias == NULL) {
+ *errp = ENODEV;
+ }
+
+ return (alias);
+}
+
+/* ARGSUSED */
+descriptor_t **
+alias_get_descriptors(int filter[], int *errp)
+{
+ return (cache_get_descriptors(DM_ALIAS, errp));
+}
+
+char *
+alias_get_name(descriptor_t *desc)
+{
+ return (desc->name);
+}
+
+/* ARGSUSED */
+nvlist_t *
+alias_get_stats(descriptor_t *dp, int stat_type, int *errp)
+{
+ /* There are no stat types defined for aliases */
+ *errp = EINVAL;
+ return (NULL);
+}
+
+int
+alias_make_descriptors()
+{
+ int error;
+ disk_t *dp;
+
+ dp = cache_get_disklist();
+ while (dp != NULL) {
+ alias_t *ap;
+
+ ap = dp->aliases;
+ while (ap != NULL) {
+ if (ap->alias != NULL) {
+ cache_load_desc(DM_ALIAS, dp, ap->alias, NULL, &error);
+ if (error != 0) {
+ return (error);
+ }
+ }
+ ap = ap->next;
+ }
+ dp = dp->next;
+ }
+
+ return (0);
+}
+
+static int
+get_status(disk_t *diskp, int fd, nvlist_t *attrs)
+{
+ struct dk_minfo minfo;
+
+ /* Make sure media is inserted and spun up. */
+ if (fd >= 0 && media_read_info(fd, &minfo)) {
+
+ if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_UP) != 0) {
+ return (ENOMEM);
+ }
+
+ } else {
+ /* Not ready, so either no media or dead. */
+
+ if (diskp->removable) {
+ /* This is a removable drive with no media. */
+ if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_UP) != 0) {
+ return (ENOMEM);
+ }
+ } else {
+ /* not removable, so must be dead */
+ if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_DOWN) != 0) {
+ return (ENOMEM);
+ }
+ }
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/libdiskmgt/common/assoc_types.c b/usr/src/lib/libdiskmgt/common/assoc_types.c
new file mode 100644
index 0000000000..2ded240374
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/assoc_types.c
@@ -0,0 +1,80 @@
+/*
+ * 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 "libdiskmgt.h"
+
+dm_desc_type_t alias_assoc_types [] = {
+ DM_DRIVE,
+ -1
+};
+
+dm_desc_type_t bus_assoc_types [] = {
+ DM_CONTROLLER,
+ DM_BUS,
+ -1
+};
+
+dm_desc_type_t controller_assoc_types [] = {
+ DM_DRIVE,
+ DM_PATH,
+ DM_BUS,
+ -1
+};
+
+dm_desc_type_t drive_assoc_types [] = {
+ DM_CONTROLLER,
+ DM_PATH,
+ DM_ALIAS,
+ DM_MEDIA,
+ -1
+};
+
+dm_desc_type_t media_assoc_types [] = {
+ DM_DRIVE,
+ DM_PARTITION,
+ DM_SLICE,
+ -1
+};
+
+dm_desc_type_t partition_assoc_types [] = {
+ DM_MEDIA,
+ DM_SLICE,
+ -1
+};
+
+dm_desc_type_t path_assoc_types [] = {
+ DM_DRIVE,
+ DM_CONTROLLER,
+ -1
+};
+
+dm_desc_type_t slice_assoc_types [] = {
+ DM_MEDIA,
+ DM_PARTITION,
+ -1
+};
diff --git a/usr/src/lib/libdiskmgt/common/bus.c b/usr/src/lib/libdiskmgt/common/bus.c
new file mode 100644
index 0000000000..2b406aa6a6
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/bus.c
@@ -0,0 +1,250 @@
+/*
+ * 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 <fcntl.h>
+#include <libdevinfo.h>
+#include <stdlib.h>
+#include <sys/sunddi.h>
+#include <sys/types.h>
+#include <sys/scsi/conf/autoconf.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+
+static descriptor_t **get_assoc_buses(descriptor_t *desc, int *errp);
+static descriptor_t **get_assoc_controllers(descriptor_t *desc, int *errp);
+
+descriptor_t **
+bus_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type, int *errp)
+{
+ switch (type) {
+ case DM_BUS:
+ return (get_assoc_buses(desc, errp));
+ case DM_CONTROLLER:
+ return (get_assoc_controllers(desc, errp));
+ }
+
+ *errp = EINVAL;
+ return (NULL);
+}
+
+nvlist_t *
+bus_get_attributes(descriptor_t *dp, int *errp)
+{
+ bus_t *bp;
+ nvlist_t *attrs;
+
+ if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ bp = dp->p.bus;
+
+ if (nvlist_add_string(attrs, DM_BTYPE, bp->btype) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ if (bp->freq != 0) {
+ if (nvlist_add_uint32(attrs, DM_CLOCK, bp->freq) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+ }
+
+ if (bp->pname != NULL) {
+ if (nvlist_add_string(attrs, DM_PNAME, bp->pname) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+ }
+
+ *errp = 0;
+ return (attrs);
+}
+
+descriptor_t *
+bus_get_descriptor_by_name(char *name, int *errp)
+{
+ descriptor_t **buses;
+ int i;
+ descriptor_t *bus = NULL;
+
+ buses = cache_get_descriptors(DM_BUS, errp);
+ if (*errp != 0) {
+ return (NULL);
+ }
+
+ for (i = 0; buses[i]; i++) {
+ if (libdiskmgt_str_eq(name, buses[i]->p.bus->name)) {
+ bus = buses[i];
+ } else {
+ /* clean up the unused descriptors */
+ cache_free_descriptor(buses[i]);
+ }
+ }
+ free(buses);
+
+ if (bus == NULL) {
+ *errp = ENODEV;
+ }
+
+ return (bus);
+}
+
+/* ARGSUSED */
+descriptor_t **
+bus_get_descriptors(int filter[], int *errp)
+{
+ return (cache_get_descriptors(DM_BUS, errp));
+}
+
+char *
+bus_get_name(descriptor_t *desc)
+{
+ return (desc->p.bus->name);
+}
+
+/* ARGSUSED */
+nvlist_t *
+bus_get_stats(descriptor_t *dp, int stat_type, int *errp)
+{
+ /* There are no stat types defined for controllers */
+ *errp = EINVAL;
+ return (NULL);
+}
+
+int
+bus_make_descriptors()
+{
+ int error;
+ bus_t *bp;
+
+ bp = cache_get_buslist();
+ while (bp != NULL) {
+ cache_load_desc(DM_BUS, bp, NULL, NULL, &error);
+ if (error != 0) {
+ return (error);
+ }
+ bp = bp->next;
+ }
+
+ return (0);
+}
+
+static descriptor_t **
+get_assoc_buses(descriptor_t *desc, int *errp)
+{
+ bus_t *bp;
+ char *name;
+ descriptor_t **allbuses;
+ descriptor_t **buses;
+ int cnt;
+ int i;
+ int pos;
+
+ bp = desc->p.bus;
+ name = bp->name;
+
+ allbuses = cache_get_descriptors(DM_BUS, errp);
+ if (*errp != 0) {
+ return (NULL);
+ }
+
+ /* Count how many we have (we overcount, but thats ok). */
+ for (cnt = 0; allbuses[cnt]; cnt++);
+
+ /* make the snapshot */
+ buses = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
+ if (buses == NULL) {
+ *errp = ENOMEM;
+ cache_free_descriptors(allbuses);
+ return (NULL);
+ }
+
+ /*
+ * Get this buses parent bus and get the buses that I am the parent of.
+ */
+ pos = 0;
+ for (i = 0; allbuses[i]; i++) {
+ if (libdiskmgt_str_eq(name, allbuses[i]->p.bus->pname)) {
+ buses[pos++] = allbuses[i];
+ } else if (bp->pname != NULL &&
+ libdiskmgt_str_eq(bp->pname, allbuses[i]->p.bus->name)) {
+
+ buses[pos++] = allbuses[i];
+ } else {
+ /* clean up the unused descriptor */
+ cache_free_descriptor(allbuses[i]);
+ }
+ }
+ buses[pos] = NULL;
+
+ free(allbuses);
+
+ *errp = 0;
+ return (buses);
+}
+
+static descriptor_t **
+get_assoc_controllers(descriptor_t *desc, int *errp)
+{
+ bus_t *bp;
+ descriptor_t **controllers;
+ int cnt;
+ int i;
+
+ bp = desc->p.bus;
+
+ /* Count how many we have. */
+ for (cnt = 0; bp->controllers[cnt]; cnt++);
+
+ /* make the snapshot */
+ controllers = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
+ if (controllers == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ for (i = 0; bp->controllers[i]; i++) {
+ controllers[i] = cache_get_desc(DM_CONTROLLER, bp->controllers[i],
+ NULL, NULL, errp);
+ if (*errp != 0) {
+ cache_free_descriptors(controllers);
+ return (NULL);
+ }
+ }
+ controllers[i] = NULL;
+
+ *errp = 0;
+ return (controllers);
+}
diff --git a/usr/src/lib/libdiskmgt/common/cache.c b/usr/src/lib/libdiskmgt/common/cache.c
new file mode 100644
index 0000000000..411d6c41b7
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/cache.c
@@ -0,0 +1,991 @@
+/*
+ * 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 <fcntl.h>
+#include <libdevinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <synch.h>
+#include <sys/sunddi.h>
+#include <sys/types.h>
+#include <libgen.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+#include "partition.h"
+
+#define ALIASES 0
+#define DEVPATHS 1
+
+/*
+ * Set DM_LIBDISKMGT_DEBUG in the environment. Two levels of debugging:
+ * 1 - errors, warnings and minimal tracing information
+ * 2 - verbose information
+ * All output prints on stderr.
+ */
+int dm_debug = 0;
+
+/* Lock protecting the cached data */
+static rwlock_t cache_lock = DEFAULTRWLOCK;
+static disk_t *disk_listp = NULL;
+static controller_t *controller_listp = NULL;
+static bus_t *bus_listp = NULL;
+static int cache_loaded = 0;
+
+descriptor_t *desc_listp = NULL;
+
+static void clear_descriptors(void *gp);
+static void clr_ctrl_disk_ptr(controller_t *cp, disk_t *dp);
+static void clr_path_disk_ptr(path_t *pp, disk_t *dp);
+static void del_drive(disk_t *dp);
+static void del_drive_by_name(char *name);
+static descriptor_t *have_desc(int type, void *gp, char *name, char *mname);
+static int initialize();
+static int make_descriptors(int type);
+static int match_disk(disk_t *oldp, disk_t *newp);
+static int match_aliases(disk_t *d1p, disk_t *d2p);
+static int match_alias(alias_t *ap, alias_t *listp);
+static descriptor_t *new_descriptor(dm_desc_type_t type, void *op,
+ char *name, char *mname);
+static void rewalk_tree();
+static void update_desc(descriptor_t *descp, disk_t *newdisksp,
+ controller_t *newctrlp, bus_t *newbusp);
+static void update_desc_busp(descriptor_t *descp, bus_t *busp);
+static void update_desc_ctrlp(descriptor_t *descp,
+ controller_t *newstrlp);
+static void update_desc_diskp(descriptor_t *descp,
+ disk_t *newdisksp);
+static void update_desc_pathp(descriptor_t *descp,
+ controller_t *newctrlp);
+
+/*
+ * We only cache some of the data that we can obtain. For much of the data
+ * (e.g. slices & disks getting repartitioned) there are no events which would
+ * enable us to cache. As more events are added we can cache more information.
+ *
+ * Currently we cache the information we get from the dev tree walk. This is
+ * basically the information about the drives, aliases, devpaths, controllers
+ * and paths. We do not cache any information related to media, partitions
+ * or slices.
+ *
+ * A fundamental part of the API design is that the application can hold on
+ * to a set of descriptors for an indeterminate amount of time. Even if the
+ * application does not hold descriptors there is a window of time between the
+ * call that gets the descriptor and the use of the descriptor to get more
+ * information. Because of this, the cache design must work even if the object
+ * that the descriptor refers to no longer exists.
+ *
+ * Given this requirement, the code implements a two level cache. The
+ * descriptors that the application gets are really pointers into the first
+ * level of the cache. This first level contains the actual descriptors.
+ * These descriptors in turn refer to the objects we build from the dev tree
+ * walk which represent the drives and controllers. This is the second level
+ * in the cache.
+ *
+ * When we update the second level of the cache (the drives and controllers)
+ * we go through the first level (the descriptors) and update the pointers
+ * in those descriptors to refer to the new objects in the second level. If
+ * the object that the descriptor referred to is no longer in existence, we
+ * just null out the pointer in the descriptor. In this way the code that
+ * uses the descriptors knows that the object referred to by the descriptor
+ * no longer exists.
+ *
+ * We keep a reference count in the descriptors. This is incremented when
+ * we hand out a pointer to the descriptor and decremented when the application
+ * frees the descriptor it has. When the reference count goes to 0 we garbage
+ * collect the descriptors. In this way we only have to update active
+ * descriptors when we refresh the cache after an event.
+ *
+ * An example of the flow when we create descriptors:
+ * dm_get_descriptors libdiskmgt.c
+ * drive_get_descriptors drive.c
+ * cache_get_descriptors cache.c
+ * make_descriptors cache.c
+ * drive_make_descriptors drive.c
+ * cache_load_desc cache.c
+ * {update refcnts on descriptors & return them}
+ *
+ * The idea behind cache_get_descriptors and cache_load_desc is that we
+ * seperate the act of making the descriptor within the cache (which requires
+ * us to call back out to one of the object functions - drive_make_descriptors)
+ * from the act of handing out the descriptor (which requires us to increment
+ * the refcnt). In this way we keep all of the refcnt handling centralized
+ * in one function instead of forcing each object to ensure it replicates
+ * the refcnt handling correctly.
+ *
+ * Descriptors use two different kinds of indrection to refer to their
+ * corresponding object. For objects we cache (controllers, paths & drives)
+ * the descriptor keeps a pointer to that object. For objects that we
+ * dynamically build, the descriptor uses a combination of a pointer to the
+ * base object (usually the drive) along with a name (e.g. the media name or
+ * the alias). For objects that are based on media (e.g. a slice) we actually
+ * have to maintain a pointer (to the disk) and two names (e.g. the slice name
+ * and the media name which is the secondary name).
+ */
+
+void
+cache_free_alias(alias_t *aliasp)
+{
+ slice_t *dp;
+
+ free(aliasp->alias);
+ free(aliasp->kstat_name);
+ free(aliasp->wwn);
+
+ /* free devpaths */
+ dp = aliasp->devpaths;
+ while (dp != NULL) {
+ slice_t *nextp;
+
+ nextp = dp->next;
+ free(dp->devpath);
+ free(dp);
+ dp = nextp;
+ }
+
+ /* free orig_paths */
+ dp = aliasp->orig_paths;
+ while (dp != NULL) {
+ slice_t *nextp;
+
+ nextp = dp->next;
+ free(dp->devpath);
+ free(dp);
+ dp = nextp;
+ }
+
+ free(aliasp);
+}
+
+void
+cache_free_bus(bus_t *bp)
+{
+ free(bp->name);
+ free(bp->btype);
+ free(bp->kstat_name);
+ free(bp->pname);
+ free(bp->controllers);
+ free(bp);
+}
+
+void
+cache_free_controller(controller_t *cp)
+{
+ free(cp->name);
+ free(cp->kstat_name);
+ free(cp->disks);
+ if (cp->paths != NULL) {
+ int i;
+
+ for (i = 0; cp->paths[i]; i++) {
+ /* free the path since it can't exist w/o the controller */
+ cache_free_path(cp->paths[i]);
+ }
+ free(cp->paths);
+ }
+
+ free(cp);
+}
+
+void
+cache_free_descriptor(descriptor_t *desc)
+{
+ if (!cache_is_valid_desc(desc)) {
+ return;
+ }
+
+ desc->refcnt--;
+
+ if (desc->refcnt <= 0) {
+ free(desc->name);
+ free(desc->secondary_name);
+ if (desc->prev == NULL) {
+ /* this is the first descriptor, update head ptr */
+ desc_listp = desc->next;
+ } else {
+ desc->prev->next = desc->next;
+ }
+ if (desc->next != NULL) {
+ desc->next->prev = desc->prev;
+ }
+ free(desc);
+ }
+}
+
+void
+cache_free_descriptors(descriptor_t **desc_list)
+{
+ int i;
+
+ for (i = 0; desc_list[i]; i++) {
+ cache_free_descriptor(desc_list[i]);
+ }
+
+ free(desc_list);
+}
+
+void
+cache_free_disk(disk_t *dp)
+{
+ alias_t *ap;
+
+ free(dp->device_id);
+ if (dp->devid != NULL) {
+ devid_free(dp->devid);
+ }
+ free(dp->kernel_name);
+ free(dp->product_id);
+ free(dp->vendor_id);
+ free(dp->controllers);
+ /* the path objects are freed when we free the controller */
+ free(dp->paths);
+ ap = dp->aliases;
+ while (ap != NULL) {
+ alias_t *nextp;
+
+ nextp = ap->next;
+ cache_free_alias(ap);
+ ap = nextp;
+ }
+
+ free(dp);
+}
+
+void
+cache_free_path(path_t *pp)
+{
+ int i;
+
+ free(pp->name);
+ free(pp->disks);
+ free(pp->states);
+
+ for (i = 0; pp->wwns[i]; i++) {
+ free(pp->wwns[i]);
+ }
+ free(pp->wwns);
+
+ free(pp);
+}
+
+bus_t *
+cache_get_buslist()
+{
+ if (initialize() != 0) {
+ return (NULL);
+ }
+
+ return (bus_listp);
+}
+
+controller_t *
+cache_get_controllerlist()
+{
+ if (initialize() != 0) {
+ return (NULL);
+ }
+
+ return (controller_listp);
+}
+
+/*
+ * This routine will either get the existing descriptor from the descriptor
+ * cache or make make a new descriptor and put it in the descriptor cache and
+ * return a pointer to that descriptor. We increment the refcnt when we hand
+ * out the descriptor.
+ */
+descriptor_t *
+cache_get_desc(int type, void *gp, char *name, char *secondary_name, int *errp)
+{
+ descriptor_t *dp;
+
+ *errp = 0;
+ if ((dp = have_desc(type, gp, name, secondary_name)) == NULL) {
+ /* make a new desc */
+ if ((dp = new_descriptor(type, gp, name, secondary_name)) == NULL) {
+ *errp = ENOMEM;
+ }
+ }
+
+ if (dp != NULL) {
+ dp->refcnt++;
+ }
+
+ return (dp);
+}
+
+descriptor_t **
+cache_get_descriptors(int type, int *errp)
+{
+ descriptor_t **descs;
+ descriptor_t *descp;
+ int cnt = 0;
+ int pos;
+
+ if ((*errp = make_descriptors(type)) != 0) {
+ return (NULL);
+ }
+
+ /* count the number of active descriptors in the descriptor cache */
+ descp = desc_listp;
+ while (descp != NULL) {
+ if (descp->type == type && descp->p.generic != NULL) {
+ cnt++;
+ }
+ descp = descp->next;
+ }
+
+ descs = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
+ if (descs == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ pos = 0;
+ descp = desc_listp;
+ while (descp != NULL) {
+ if (descp->type == type && descp->p.generic != NULL) {
+ /* update refcnts before handing out the descriptors */
+ descp->refcnt++;
+ descs[pos++] = descp;
+ }
+ descp = descp->next;
+ }
+ descs[pos] = NULL;
+
+ *errp = 0;
+ return (descs);
+}
+
+disk_t *
+cache_get_disklist()
+{
+ if (initialize() != 0) {
+ return (NULL);
+ }
+
+ return (disk_listp);
+}
+
+int
+cache_is_valid_desc(descriptor_t *d)
+{
+ descriptor_t *descp;
+
+ for (descp = desc_listp; descp != NULL; descp = descp->next) {
+ if (descp == d) {
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * This function is called by the *_make_descriptors function
+ * (e.g. drive_make_descriptors) within each of the objects. This function
+ * makes sure that the descriptor is built in the descriptor cache but
+ * it does not hand out the descriptors, so the refcnt is never incremented.
+ */
+void
+cache_load_desc(int type, void *gp, char *name, char *secondary_name, int *errp)
+{
+ *errp = 0;
+ if (have_desc(type, gp, name, secondary_name) == NULL) {
+ /* make a new desc */
+ if (new_descriptor(type, gp, name, secondary_name) == NULL) {
+ *errp = ENOMEM;
+ }
+ }
+}
+
+void
+cache_rlock()
+{
+ (void) rw_rdlock(&cache_lock);
+}
+
+void
+cache_unlock()
+{
+ (void) rw_unlock(&cache_lock);
+}
+
+/*
+ * This function is called when we get a devtree event. Type is either add
+ * or delete of a drive.
+ *
+ * For delete, we need to clean up the 2nd level structures and clean up
+ * the pointers between the them. We also clear the descriptor ptr.
+ */
+void
+cache_update(dm_event_type_t ev_type, char *devname)
+{
+ char *orig_name;
+
+ cache_wlock();
+
+ /* update the cache */
+ switch (ev_type) {
+ case DM_EV_DISK_ADD:
+ rewalk_tree();
+ events_new_event(devname, DM_DRIVE, DM_EV_TADD);
+ break;
+ case DM_EV_DISK_DELETE:
+ orig_name = devname;
+ devname = basename(devname);
+ del_drive_by_name(devname);
+ events_new_event(orig_name, DM_DRIVE, DM_EV_TREMOVE);
+ break;
+ }
+
+ cache_unlock();
+}
+
+void
+cache_wlock()
+{
+ (void) rw_wrlock(&cache_lock);
+}
+
+/*
+ * Clear any descriptors that point at the specified cached object.
+ * We must go through the whole list since there can be multiple descriptors
+ * referencing the same object (i.e. drive/media/slice descriptors all point
+ * to the same drive object). The list is usually small (0 size) so this
+ * is not a big deal.
+ */
+static void
+clear_descriptors(void *gp)
+{
+ descriptor_t *descp;
+
+ for (descp = desc_listp; descp != NULL; descp = descp->next) {
+ if (descp->p.generic == gp) {
+ /* clear descriptor */
+ descp->p.generic = NULL;
+ }
+ }
+}
+
+/* remove the ptr from the controller to the specified disk */
+static void
+clr_ctrl_disk_ptr(controller_t *cp, disk_t *dp)
+{
+ int i;
+
+ for (i = 0; cp->disks[i]; i++) {
+ if (dp == cp->disks[i]) {
+ int j;
+
+ for (j = i; cp->disks[j]; j++) {
+ cp->disks[j] = cp->disks[j + 1];
+ }
+ return;
+ }
+ }
+}
+
+/* remove the ptr from the path to the specified disk */
+static void
+clr_path_disk_ptr(path_t *pp, disk_t *dp)
+{
+ int i;
+
+ for (i = 0; pp->disks[i]; i++) {
+ if (dp == pp->disks[i]) {
+ int j;
+
+ for (j = i; pp->disks[j]; j++) {
+ pp->disks[j] = pp->disks[j + 1];
+ }
+ return;
+ }
+ }
+}
+
+static void
+del_drive(disk_t *dp)
+{
+ int i;
+ disk_t *listp;
+ disk_t *prev = NULL;
+
+ clear_descriptors(dp);
+
+ /* clear any ptrs from controllers to this drive */
+ if (dp->controllers != NULL) {
+ for (i = 0; dp->controllers[i]; i++) {
+ clr_ctrl_disk_ptr(dp->controllers[i], dp);
+ }
+ }
+
+ /* clear any ptrs from paths to this drive */
+ if (dp->paths != NULL) {
+ for (i = 0; dp->paths[i]; i++) {
+ clr_path_disk_ptr(dp->paths[i], dp);
+ }
+ }
+
+ /* clear drive from disk list */
+ for (listp = disk_listp; listp != NULL; listp = listp->next) {
+ if (dp == listp) {
+ if (prev == NULL) {
+ disk_listp = dp->next;
+ } else {
+ prev->next = dp->next;
+ }
+
+ break;
+ }
+
+ if (prev == NULL) {
+ prev = disk_listp;
+ } else {
+ prev = prev->next;
+ }
+ }
+
+ cache_free_disk(dp);
+}
+
+/*
+ * Delete cached drive info when we get a devtree drive delete event.
+ */
+static void
+del_drive_by_name(char *name)
+{
+ disk_t *listp;
+
+ for (listp = disk_listp; listp != NULL; listp = listp->next) {
+ alias_t *ap;
+
+ for (ap = listp->aliases; ap; ap = ap->next) {
+ if (libdiskmgt_str_eq(name, ap->alias)) {
+ del_drive(listp);
+ return;
+ }
+ }
+ }
+}
+
+static descriptor_t *
+have_desc(int type, void *gp, char *name, char *secondary_name)
+{
+ descriptor_t *descp;
+
+ if (name != NULL && name[0] == 0) {
+ name = NULL;
+ }
+
+ if (secondary_name != NULL && secondary_name[0] == 0) {
+ secondary_name = NULL;
+ }
+
+ descp = desc_listp;
+ while (descp != NULL) {
+ if (descp->type == type && descp->p.generic == gp &&
+ libdiskmgt_str_eq(descp->name, name)) {
+ if (type == DM_SLICE || type == DM_PARTITION ||
+ type == DM_PATH) {
+ if (libdiskmgt_str_eq(descp->secondary_name,
+ secondary_name)) {
+ return (descp);
+ }
+ } else {
+ return (descp);
+ }
+ }
+ descp = descp->next;
+ }
+
+ return (NULL);
+}
+
+static int
+initialize()
+{
+ struct search_args args;
+ int status;
+
+ if (cache_loaded) {
+ return (0);
+ }
+
+ libdiskmgt_init_debug();
+
+ findevs(&args);
+
+ if (args.dev_walk_status != 0) {
+ return (args.dev_walk_status);
+ }
+
+ disk_listp = args.disk_listp;
+ controller_listp = args.controller_listp;
+ bus_listp = args.bus_listp;
+
+ cache_loaded = 1;
+
+ if ((status = events_start_event_watcher()) != 0) {
+ return (status);
+ }
+
+ return (0);
+}
+
+static int
+make_descriptors(int type)
+{
+ int error;
+
+ if ((error = initialize()) != 0) {
+ return (error);
+ }
+
+ switch (type) {
+ case DM_DRIVE:
+ error = drive_make_descriptors();
+ break;
+ case DM_BUS:
+ error = bus_make_descriptors();
+ break;
+ case DM_CONTROLLER:
+ error = controller_make_descriptors();
+ break;
+ case DM_PATH:
+ error = path_make_descriptors();
+ break;
+ case DM_ALIAS:
+ error = alias_make_descriptors();
+ break;
+ case DM_MEDIA:
+ error = media_make_descriptors();
+ break;
+ case DM_PARTITION:
+ error = partition_make_descriptors();
+ break;
+ case DM_SLICE:
+ error = slice_make_descriptors();
+ break;
+ }
+
+ return (error);
+}
+
+static int
+match_alias(alias_t *ap, alias_t *listp)
+{
+ if (ap->alias == NULL) {
+ return (0);
+ }
+
+ while (listp != NULL) {
+ if (libdiskmgt_str_eq(ap->alias, listp->alias)) {
+ return (1);
+ }
+ listp = listp->next;
+ }
+
+ return (0);
+}
+
+static int
+match_aliases(disk_t *d1p, disk_t *d2p)
+{
+ alias_t *ap;
+
+ if (d1p->aliases == NULL || d2p->aliases == NULL) {
+ return (0);
+ }
+
+ ap = d1p->aliases;
+ while (ap != NULL) {
+ if (match_alias(ap, d2p->aliases)) {
+ return (1);
+ }
+ ap = ap->next;
+ }
+
+ return (0);
+}
+
+static int
+match_disk(disk_t *oldp, disk_t *newp)
+{
+ if (oldp->devid != NULL) {
+ if (newp->devid != NULL &&
+ devid_compare(oldp->devid, newp->devid) == 0) {
+ return (1);
+ }
+
+ } else {
+ /* oldp device id is null */
+ if (newp->devid == NULL) {
+ /* both disks have no device id, check aliases */
+ if (match_aliases(oldp, newp)) {
+ return (1);
+ }
+ }
+ }
+
+ return (0);
+}
+
+static descriptor_t *
+new_descriptor(dm_desc_type_t type, void *op, char *name, char *secondary_name)
+{
+ descriptor_t *d;
+
+ if (name != NULL && name[0] == 0) {
+ name = NULL;
+ }
+
+ if (secondary_name != NULL && secondary_name[0] == 0) {
+ secondary_name = NULL;
+ }
+
+ d = (descriptor_t *)malloc(sizeof (descriptor_t));
+ if (d == NULL) {
+ return (NULL);
+ }
+ d->type = type;
+ switch (type) {
+ case DM_CONTROLLER:
+ d->p.controller = op;
+ break;
+ case DM_BUS:
+ d->p.bus = op;
+ break;
+ default:
+ d->p.disk = op;
+ break;
+ }
+ if (name != NULL) {
+ d->name = strdup(name);
+ if (d->name == NULL) {
+ free(d);
+ return (NULL);
+ }
+ } else {
+ d->name = NULL;
+ }
+
+ if (type == DM_SLICE || type == DM_PARTITION) {
+ if (secondary_name != NULL) {
+ d->secondary_name = strdup(secondary_name);
+ if (d->secondary_name == NULL) {
+ free(d->name);
+ free(d);
+ return (NULL);
+ }
+ } else {
+ d->secondary_name = NULL;
+ }
+ } else {
+ d->secondary_name = NULL;
+ }
+
+ d->refcnt = 0;
+
+ /* add this descriptor to the head of the list */
+ if (desc_listp != NULL) {
+ desc_listp->prev = d;
+ }
+ d->prev = NULL;
+ d->next = desc_listp;
+ desc_listp = d;
+
+ return (d);
+}
+
+static void
+rewalk_tree()
+{
+ struct search_args args;
+ disk_t *free_disklistp;
+ controller_t *free_controllerlistp;
+ bus_t *free_buslistp;
+
+ findevs(&args);
+
+ if (args.dev_walk_status == 0) {
+ descriptor_t *descp;
+
+ /* walk the existing descriptors and update the ptrs */
+ descp = desc_listp;
+ while (descp != NULL) {
+ update_desc(descp, args.disk_listp, args.controller_listp,
+ args.bus_listp);
+ descp = descp->next;
+ }
+
+ /* update the cached object ptrs */
+ free_disklistp = disk_listp;
+ free_controllerlistp = controller_listp;
+ free_buslistp = bus_listp;
+ disk_listp = args.disk_listp;
+ controller_listp = args.controller_listp;
+ bus_listp = args.bus_listp;
+
+ } else {
+ free_disklistp = args.disk_listp;
+ free_controllerlistp = args.controller_listp;
+ free_buslistp = args.bus_listp;
+ }
+
+ /*
+ * Free the memory from either the old cached objects or the failed
+ * update objects.
+ */
+ while (free_disklistp != NULL) {
+ disk_t *nextp;
+
+ nextp = free_disklistp->next;
+ cache_free_disk(free_disklistp);
+ free_disklistp = nextp;
+ }
+ while (free_controllerlistp != NULL) {
+ controller_t *nextp;
+
+ nextp = free_controllerlistp->next;
+ cache_free_controller(free_controllerlistp);
+ free_controllerlistp = nextp;
+ }
+ while (free_buslistp != NULL) {
+ bus_t *nextp;
+
+ nextp = free_buslistp->next;
+ cache_free_bus(free_buslistp);
+ free_buslistp = nextp;
+ }
+}
+
+/*
+ * Walk the new set of cached objects and update the descriptor ptr to point
+ * to the correct new object. If there is no object any more, set the desc
+ * ptr to null.
+ */
+static void
+update_desc(descriptor_t *descp, disk_t *newdisksp, controller_t *newctrlp,
+ bus_t *newbusp)
+{
+ /* if the descriptor is already dead, we're done */
+ if (descp->p.generic == NULL) {
+ return;
+ }
+
+ /*
+ * All descriptors use a disk ptr except for controller descriptors
+ * and path descriptors.
+ */
+
+ switch (descp->type) {
+ case DM_BUS:
+ update_desc_busp(descp, newbusp);
+ break;
+ case DM_CONTROLLER:
+ update_desc_ctrlp(descp, newctrlp);
+ break;
+ case DM_PATH:
+ update_desc_pathp(descp, newctrlp);
+ break;
+ default:
+ update_desc_diskp(descp, newdisksp);
+ break;
+ }
+}
+
+static void
+update_desc_busp(descriptor_t *descp, bus_t *busp)
+{
+ /* walk the new objects and find the correct bus */
+ for (; busp; busp = busp->next) {
+ if (libdiskmgt_str_eq(descp->p.bus->name, busp->name)) {
+ descp->p.bus = busp;
+ return;
+ }
+ }
+
+ /* we did not find the controller any more, clear the ptr in the desc */
+ descp->p.bus = NULL;
+}
+
+static void
+update_desc_ctrlp(descriptor_t *descp, controller_t *newctrlp)
+{
+ /* walk the new objects and find the correct controller */
+ for (; newctrlp; newctrlp = newctrlp->next) {
+ if (libdiskmgt_str_eq(descp->p.controller->name, newctrlp->name)) {
+ descp->p.controller = newctrlp;
+ return;
+ }
+ }
+
+ /* we did not find the controller any more, clear the ptr in the desc */
+ descp->p.controller = NULL;
+}
+
+static void
+update_desc_diskp(descriptor_t *descp, disk_t *newdisksp)
+{
+ /* walk the new objects and find the correct disk */
+ for (; newdisksp; newdisksp = newdisksp->next) {
+ if (match_disk(descp->p.disk, newdisksp)) {
+ descp->p.disk = newdisksp;
+ return;
+ }
+ }
+
+ /* we did not find the disk any more, clear the ptr in the descriptor */
+ descp->p.disk = NULL;
+}
+
+static void
+update_desc_pathp(descriptor_t *descp, controller_t *newctrlp)
+{
+ /* walk the new objects and find the correct path */
+ for (; newctrlp; newctrlp = newctrlp->next) {
+ path_t **pp;
+
+ pp = newctrlp->paths;
+ if (pp != NULL) {
+ int i;
+
+ for (i = 0; pp[i]; i++) {
+ if (libdiskmgt_str_eq(descp->p.path->name, pp[i]->name)) {
+ descp->p.path = pp[i];
+ return;
+ }
+ }
+ }
+ }
+
+ /* we did not find the path any more, clear the ptr in the desc */
+ descp->p.path = NULL;
+}
diff --git a/usr/src/lib/libdiskmgt/common/controller.c b/usr/src/lib/libdiskmgt/common/controller.c
new file mode 100644
index 0000000000..bde953f724
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/controller.c
@@ -0,0 +1,309 @@
+/*
+ * 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 <fcntl.h>
+#include <libdevinfo.h>
+#include <stdlib.h>
+#include <sys/sunddi.h>
+#include <sys/types.h>
+#include <sys/scsi/conf/autoconf.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+
+static descriptor_t **get_assoc_buses(descriptor_t *desc, int *errp);
+static descriptor_t **get_assoc_drives(descriptor_t *desc, int *errp);
+static descriptor_t **get_assoc_paths(descriptor_t *desc, int *errp);
+
+descriptor_t **
+controller_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type,
+ int *errp)
+{
+ switch (type) {
+ case DM_DRIVE:
+ return (get_assoc_drives(desc, errp));
+ case DM_PATH:
+ return (get_assoc_paths(desc, errp));
+ case DM_BUS:
+ return (get_assoc_buses(desc, errp));
+ }
+
+ *errp = EINVAL;
+ return (NULL);
+}
+
+nvlist_t *
+controller_get_attributes(descriptor_t *dp, int *errp)
+{
+ controller_t *cp;
+ nvlist_t *attrs;
+
+ if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ cp = dp->p.controller;
+
+ if (nvlist_add_string(attrs, DM_CTYPE, cp->ctype) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ if (cp->multiplex) {
+ if (nvlist_add_boolean(attrs, DM_MULTIPLEX) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+ }
+
+ if (cp->scsi_options != -1) {
+ if (cp->scsi_options & SCSI_OPTIONS_FAST) {
+ if (nvlist_add_boolean(attrs, DM_FAST) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+ }
+ if (cp->scsi_options & SCSI_OPTIONS_WIDE) {
+ if (nvlist_add_boolean(attrs, DM_WIDE) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+ }
+ if (cp->scsi_options & SCSI_OPTIONS_FAST20) {
+ if (nvlist_add_boolean(attrs, DM_FAST20) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+ }
+ if (cp->scsi_options & SCSI_OPTIONS_FAST40) {
+ if (nvlist_add_boolean(attrs, DM_FAST40) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+ }
+ if (cp->scsi_options & SCSI_OPTIONS_FAST80) {
+ if (nvlist_add_boolean(attrs, DM_FAST80) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+ }
+ }
+
+ if (cp->freq != 0) {
+ if (nvlist_add_uint32(attrs, DM_CLOCK, cp->freq) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+ }
+
+ *errp = 0;
+ return (attrs);
+}
+
+descriptor_t *
+controller_get_descriptor_by_name(char *name, int *errp)
+{
+ descriptor_t **controllers;
+ int i;
+ descriptor_t *controller = NULL;
+
+ controllers = cache_get_descriptors(DM_CONTROLLER, errp);
+ if (*errp != 0) {
+ return (NULL);
+ }
+
+ for (i = 0; controllers[i]; i++) {
+ if (libdiskmgt_str_eq(name, controllers[i]->p.controller->name)) {
+ controller = controllers[i];
+ } else {
+ /* clean up the unused descriptors */
+ cache_free_descriptor(controllers[i]);
+ }
+ }
+ free(controllers);
+
+ if (controller == NULL) {
+ *errp = ENODEV;
+ }
+
+ return (controller);
+}
+
+/* ARGSUSED */
+descriptor_t **
+controller_get_descriptors(int filter[], int *errp)
+{
+ return (cache_get_descriptors(DM_CONTROLLER, errp));
+}
+
+char *
+controller_get_name(descriptor_t *desc)
+{
+ return (desc->p.controller->name);
+}
+
+/* ARGSUSED */
+nvlist_t *
+controller_get_stats(descriptor_t *dp, int stat_type, int *errp)
+{
+ /* There are no stat types defined for controllers */
+ *errp = EINVAL;
+ return (NULL);
+}
+
+int
+controller_make_descriptors()
+{
+ int error;
+ controller_t *cp;
+
+ cp = cache_get_controllerlist();
+ while (cp != NULL) {
+ cache_load_desc(DM_CONTROLLER, cp, NULL, NULL, &error);
+ if (error != 0) {
+ return (error);
+ }
+ cp = cp->next;
+ }
+
+ return (0);
+}
+
+static descriptor_t **
+get_assoc_buses(descriptor_t *desc, int *errp)
+{
+ controller_t *cp;
+ descriptor_t **buses;
+ int pos = 0;
+
+ cp = desc->p.controller;
+
+ /* make the snapshot */
+ buses = (descriptor_t **)calloc(2, sizeof (descriptor_t *));
+ if (buses == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ if (cp->bus != NULL) {
+ buses[pos++] = cache_get_desc(DM_BUS, cp->bus, NULL, NULL, errp);
+ if (*errp != 0) {
+ cache_free_descriptors(buses);
+ return (NULL);
+ }
+ }
+ buses[pos] = NULL;
+
+ *errp = 0;
+ return (buses);
+}
+
+static descriptor_t **
+get_assoc_drives(descriptor_t *desc, int *errp)
+{
+ controller_t *cp;
+ descriptor_t **drives;
+ int cnt;
+ int i;
+
+ cp = desc->p.controller;
+
+ /* Count how many we have. */
+ for (cnt = 0; cp->disks[cnt]; cnt++);
+
+ /* make the snapshot */
+ drives = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
+ if (drives == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ for (i = 0; cp->disks[i]; i++) {
+ drives[i] = cache_get_desc(DM_DRIVE, cp->disks[i], NULL, NULL,
+ errp);
+ if (*errp != 0) {
+ cache_free_descriptors(drives);
+ return (NULL);
+ }
+ }
+ drives[i] = NULL;
+
+ *errp = 0;
+ return (drives);
+}
+
+static descriptor_t **
+get_assoc_paths(descriptor_t *desc, int *errp)
+{
+ path_t **pp;
+ int cnt;
+ descriptor_t **paths;
+ int i;
+
+ pp = desc->p.controller->paths;
+
+ /* Count how many we have. */
+ cnt = 0;
+ if (pp != NULL) {
+ for (; pp[cnt]; cnt++);
+ }
+
+ /* make the snapshot */
+ paths = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
+ if (paths == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ /*
+ * The name field of the descriptor is not filled in. Thus, we
+ * know not to try to lookup drive-path state information within
+ * the path code if we try to get attributes for this descriptor.
+ */
+ for (i = 0; i < cnt; i++) {
+ paths[i] = cache_get_desc(DM_PATH, pp[i], NULL, NULL, errp);
+ if (*errp != 0) {
+ cache_free_descriptors(paths);
+ return (NULL);
+ }
+ }
+
+ paths[i] = NULL;
+
+ *errp = 0;
+ return (paths);
+}
diff --git a/usr/src/lib/libdiskmgt/common/disks_private.h b/usr/src/lib/libdiskmgt/common/disks_private.h
new file mode 100644
index 0000000000..3239925853
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/disks_private.h
@@ -0,0 +1,273 @@
+/*
+ * 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.
+ */
+
+#ifndef _DISKS_PRIVATE_H
+#define _DISKS_PRIVATE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libdevinfo.h>
+#include <sys/dkio.h>
+#include <devid.h>
+
+#define DM_DEBUG "DM_LIBDISKMGT_DEBUG"
+extern int dm_debug;
+
+#define NVATTRS NV_UNIQUE_NAME | NV_UNIQUE_NAME_TYPE
+#define NVATTRS_STAT 0x0
+
+typedef struct slice_info {
+ char *devpath;
+ int slice_num;
+ struct slice_info *next;
+} slice_t;
+
+typedef struct alias_info {
+ char *kstat_name;
+ char *alias;
+ slice_t *devpaths;
+ slice_t *orig_paths;
+ char *wwn;
+ int cluster;
+ int lun;
+ int target;
+ struct alias_info *next;
+} alias_t;
+
+typedef struct path {
+ char *name;
+ char *ctype;
+ int *states;
+ char **wwns;
+ struct disk **disks;
+ struct controller_info *controller;
+ struct path *next;
+} path_t;
+
+typedef struct bus_info {
+ char *name;
+ char *kstat_name;
+ char *btype;
+ char *pname;
+ int freq;
+ struct controller_info **controllers;
+ struct bus_info *next;
+} bus_t;
+
+typedef struct controller_info {
+ char *name;
+ char *kstat_name;
+ char *ctype;
+ int freq;
+ struct disk **disks;
+ struct path **paths;
+ struct bus_info *bus;
+ struct controller_info *next;
+ int multiplex;
+ int scsi_options;
+} controller_t;
+
+typedef struct disk {
+ char *device_id; /* string encoded device id */
+ ddi_devid_t devid; /* decoded device id */
+ char *kernel_name; /* handles drives w/ no devlinks */
+ char *volm_path;
+ char *product_id;
+ char *vendor_id;
+ controller_t **controllers;
+ path_t **paths;
+ alias_t *aliases;
+ struct disk *next;
+ int drv_type;
+ int removable;
+ int sync_speed;
+ int rpm;
+ int wide;
+ int cd_rom;
+ int volm_path_set;
+} disk_t;
+
+typedef struct descriptor {
+ union {
+ void *generic;
+ disk_t *disk;
+ controller_t *controller;
+ bus_t *bus;
+ path_t *path;
+ } p;
+ char *name;
+ char *secondary_name;
+ struct descriptor *next;
+ struct descriptor *prev;
+ dm_desc_type_t type;
+ int refcnt;
+} descriptor_t;
+
+struct search_args {
+ disk_t *disk_listp;
+ controller_t *controller_listp;
+ bus_t *bus_listp;
+ di_devlink_handle_t handle;
+ di_prom_handle_t ph;
+ di_node_t node;
+ di_minor_t minor;
+ int dev_walk_status;
+};
+
+typedef enum {
+ DM_EV_DISK_ADD = 0,
+ DM_EV_DISK_DELETE
+} dm_event_type_t;
+
+
+/* private internal functions */
+descriptor_t **alias_get_descriptors(int filter[], int *errp);
+descriptor_t **alias_get_assoc_descriptors(descriptor_t *desc,
+ dm_desc_type_t type, int *errp);
+descriptor_t *alias_get_descriptor_by_name(char *name, int *errp);
+char *alias_get_name(descriptor_t *desc);
+nvlist_t *alias_get_attributes(descriptor_t *desc, int *errp);
+nvlist_t *alias_get_stats(descriptor_t *desc, int stat_type, int *errp);
+int alias_make_descriptors();
+
+descriptor_t **bus_get_descriptors(int filter[], int *errp);
+descriptor_t **bus_get_assoc_descriptors(descriptor_t *desc,
+ dm_desc_type_t type, int *errp);
+descriptor_t *bus_get_descriptor_by_name(char *name, int *errp);
+char *bus_get_name(descriptor_t *desc);
+nvlist_t *bus_get_attributes(descriptor_t *desc, int *errp);
+nvlist_t *bus_get_stats(descriptor_t *desc, int stat_type,
+ int *errp);
+int bus_make_descriptors();
+
+descriptor_t **controller_get_descriptors(int filter[], int *errp);
+descriptor_t **controller_get_assoc_descriptors(descriptor_t *desc,
+ dm_desc_type_t type, int *errp);
+descriptor_t *controller_get_descriptor_by_name(char *name, int *errp);
+char *controller_get_name(descriptor_t *desc);
+nvlist_t *controller_get_attributes(descriptor_t *desc, int *errp);
+nvlist_t *controller_get_stats(descriptor_t *desc, int stat_type,
+ int *errp);
+int controller_make_descriptors();
+
+descriptor_t **drive_get_descriptors(int filter[], int *errp);
+descriptor_t **drive_get_assoc_descriptors(descriptor_t *desc,
+ dm_desc_type_t type, int *errp);
+descriptor_t **drive_get_assocs(descriptor_t *desc, int *errp);
+descriptor_t *drive_get_descriptor_by_name(char *name, int *errp);
+char *drive_get_name(descriptor_t *desc);
+nvlist_t *drive_get_attributes(descriptor_t *desc, int *errp);
+nvlist_t *drive_get_stats(descriptor_t *desc, int stat_type, int *errp);
+int drive_make_descriptors();
+int drive_open_disk(disk_t *diskp, char *opath, int len);
+
+descriptor_t **media_get_descriptors(int filter[], int *errp);
+descriptor_t **media_get_assoc_descriptors(descriptor_t *desc,
+ dm_desc_type_t type, int *errp);
+descriptor_t **media_get_assocs(descriptor_t *desc, int *errp);
+descriptor_t *media_get_descriptor_by_name(char *name, int *errp);
+char *media_get_name(descriptor_t *desc);
+nvlist_t *media_get_attributes(descriptor_t *desc, int *errp);
+nvlist_t *media_get_stats(descriptor_t *desc, int stat_type, int *errp);
+int media_get_volm_path(disk_t *diskp, char *mediapath, int size);
+int media_make_descriptors();
+int media_read_info(int fd, struct dk_minfo *minfo);
+int media_read_name(disk_t *dp, char *mname, int size);
+
+descriptor_t **path_get_descriptors(int filter[], int *errp);
+descriptor_t **path_get_assoc_descriptors(descriptor_t *desc,
+ dm_desc_type_t type, int *errp);
+descriptor_t *path_get_descriptor_by_name(char *name, int *errp);
+char *path_get_name(descriptor_t *desc);
+nvlist_t *path_get_attributes(descriptor_t *desc, int *errp);
+nvlist_t *path_get_stats(descriptor_t *desc, int stat_type, int *errp);
+int path_make_descriptors();
+
+descriptor_t **slice_get_descriptors(int filter[], int *errp);
+descriptor_t **slice_get_assoc_descriptors(descriptor_t *desc,
+ dm_desc_type_t type, int *errp);
+descriptor_t **slice_get_assocs(descriptor_t *desc, int *errp);
+descriptor_t *slice_get_descriptor_by_name(char *name, int *errp);
+char *slice_get_name(descriptor_t *desc);
+nvlist_t *slice_get_attributes(descriptor_t *desc, int *errp);
+nvlist_t *slice_get_stats(descriptor_t *desc, int stat_type, int *errp);
+int slice_make_descriptors();
+void slice_rdsk2dsk(char *rdsk, char *dsk, int size);
+
+/* cache.c */
+void cache_free_alias(alias_t *aliasp);
+void cache_free_bus(bus_t *bp);
+void cache_free_controller(controller_t *cp);
+void cache_free_descriptor(descriptor_t *desc);
+void cache_free_descriptors(descriptor_t **desc_list);
+void cache_free_disk(disk_t *dp);
+void cache_free_path(path_t *pp);
+bus_t *cache_get_buslist();
+controller_t *cache_get_controllerlist();
+descriptor_t *cache_get_desc(int type, void *gp, char *name,
+ char *secondary_name, int *errp);
+descriptor_t **cache_get_descriptors(int type, int *errp);
+disk_t *cache_get_disklist();
+int cache_is_valid_desc(descriptor_t *d);
+void cache_load_desc(int type, void *gp, char *name,
+ char *secondary_name, int *errp);
+void cache_rlock();
+void cache_unlock();
+void cache_update(dm_event_type_t ev_type, char *devname);
+void cache_wlock();
+
+/* findevs.c */
+void findevs(struct search_args *args);
+
+/* events.c */
+int events_start_event_watcher();
+void events_new_event(char *name, int dtype, char *etype);
+void events_new_slice_event(char *dev, char *type);
+
+/* entry.c */
+void libdiskmgt_add_str(nvlist_t *attrs, char *name, char *val,
+ int *errp);
+descriptor_t **libdiskmgt_empty_desc_array(int *errp);
+void libdiskmgt_init_debug();
+int libdiskmgt_str_eq(char *nm1, char *nm2);
+
+/* in-use detectors */
+int inuse_mnt(char *slice, nvlist_t *attrs, int *errp);
+int inuse_svm(char *slice, nvlist_t *attrs, int *errp);
+int inuse_lu(char *slice, nvlist_t *attrs, int *errp);
+int inuse_dump(char *slice, nvlist_t *attrs, int *errp);
+int inuse_vxvm(char *slice, nvlist_t *attrs, int *errp);
+int inuse_fs(char *slice, nvlist_t *attrs, int *errp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DISKS_PRIVATE_H */
diff --git a/usr/src/lib/libdiskmgt/common/drive.c b/usr/src/lib/libdiskmgt/common/drive.c
new file mode 100644
index 0000000000..00fd979ae3
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/drive.c
@@ -0,0 +1,1575 @@
+/*
+ * 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 <fcntl.h>
+#include <libdevinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stropts.h>
+#include <sys/dkio.h>
+#include <sys/sunddi.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <kstat.h>
+#include <errno.h>
+#include <devid.h>
+#include <dirent.h>
+
+/* included for uscsi */
+#include <strings.h>
+#include <sys/stat.h>
+#include <sys/scsi/impl/types.h>
+#include <sys/scsi/impl/uscsi.h>
+#include <sys/scsi/generic/commands.h>
+#include <sys/scsi/impl/commands.h>
+#include <sys/scsi/generic/mode.h>
+#include <sys/byteorder.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+
+#define KSTAT_CLASS_DISK "disk"
+#define KSTAT_CLASS_ERROR "device_error"
+
+#define SCSIBUFLEN 0xffff
+
+/* byte get macros */
+#define b3(a) (((a)>>24) & 0xFF)
+#define b2(a) (((a)>>16) & 0xFF)
+#define b1(a) (((a)>>8) & 0xFF)
+#define b0(a) (((a)>>0) & 0xFF)
+
+static char *kstat_err_names[] = {
+ "Soft Errors",
+ "Hard Errors",
+ "Transport Errors",
+ "Media Error",
+ "Device Not Ready",
+ "No Device",
+ "Recoverable",
+ "Illegal Request",
+ "Predictive Failure Analysis",
+ NULL
+};
+
+static char *err_attr_names[] = {
+ DM_NSOFTERRS,
+ DM_NHARDERRS,
+ DM_NTRANSERRS,
+ DM_NMEDIAERRS,
+ DM_NDNRERRS,
+ DM_NNODEVERRS,
+ DM_NRECOVERRS,
+ DM_NILLREQERRS,
+ DM_FAILING,
+ NULL
+};
+
+/*
+ * **************** begin uscsi stuff ****************
+ */
+
+#if defined(_BIT_FIELDS_LTOH)
+#elif defined(_BIT_FIELDS_HTOL)
+#else
+#error One of _BIT_FIELDS_LTOH or _BIT_FIELDS_HTOL must be defined
+#endif
+
+struct conf_feature {
+ uchar_t feature[2]; /* common to all */
+#if defined(_BIT_FIELDS_LTOH)
+ uchar_t current : 1;
+ uchar_t persist : 1;
+ uchar_t version : 4;
+ uchar_t reserved: 2;
+#else
+ uchar_t reserved: 2;
+ uchar_t version : 4;
+ uchar_t persist : 1;
+ uchar_t current : 1;
+#endif /* _BIT_FIELDS_LTOH */
+ uchar_t len;
+ union features {
+ struct generic {
+ uchar_t data[1];
+ } gen;
+ uchar_t data[1];
+ struct profile_list {
+ uchar_t profile[2];
+#if defined(_BIT_FIELDS_LTOH)
+ uchar_t current_p : 1;
+ uchar_t reserved1 : 7;
+#else
+ uchar_t reserved1 : 7;
+ uchar_t current_p : 1;
+#endif /* _BIT_FIELDS_LTOH */
+ uchar_t reserved2;
+ } plist[1];
+ struct core {
+ uchar_t phys[4];
+ } core;
+ struct morphing {
+#if defined(_BIT_FIELDS_LTOH)
+ uchar_t async : 1;
+ uchar_t reserved1 : 7;
+#else
+ uchar_t reserved1 : 7;
+ uchar_t async : 1;
+#endif /* _BIT_FIELDS_LTOH */
+ uchar_t reserved[3];
+ } morphing;
+ struct removable {
+#if defined(_BIT_FIELDS_LTOH)
+ uchar_t lock : 1;
+ uchar_t resv1 : 1;
+ uchar_t pvnt : 1;
+ uchar_t eject : 1;
+ uchar_t resv2 : 1;
+ uchar_t loading : 3;
+#else
+ uchar_t loading : 3;
+ uchar_t resv2 : 1;
+ uchar_t eject : 1;
+ uchar_t pvnt : 1;
+ uchar_t resv1 : 1;
+ uchar_t lock : 1;
+#endif /* _BIT_FIELDS_LTOH */
+ uchar_t reserved[3];
+ } removable;
+ struct random_readable {
+ uchar_t lbsize[4];
+ uchar_t blocking[2];
+#if defined(_BIT_FIELDS_LTOH)
+ uchar_t pp : 1;
+ uchar_t reserved1 : 7;
+#else
+ uchar_t reserved1 : 7;
+ uchar_t pp : 1;
+#endif /* _BIT_FIELDS_LTOH */
+ uchar_t reserved;
+ } rread;
+ struct cd_read {
+#if defined(_BIT_FIELDS_LTOH)
+ uchar_t cdtext : 1;
+ uchar_t c2flag : 1;
+ uchar_t reserved1 : 6;
+#else
+ uchar_t reserved1 : 6;
+ uchar_t c2flag : 1;
+ uchar_t cdtext : 1;
+#endif /* _BIT_FIELDS_LTOH */
+ } cdread;
+ struct cd_audio {
+#if defined(_BIT_FIELDS_LTOH)
+ uchar_t sv : 1;
+ uchar_t scm : 1;
+ uchar_t scan : 1;
+ uchar_t resv : 5;
+#else
+ uchar_t resv : 5;
+ uchar_t scan : 1;
+ uchar_t scm : 1;
+ uchar_t sv : 1;
+#endif /* _BIT_FIELDS_LTOH */
+ uchar_t reserved;
+ uchar_t numlevels[2];
+ } audio;
+ struct dvd_css {
+ uchar_t reserved[3];
+ uchar_t version;
+ } dvdcss;
+ } features;
+};
+
+#define PROF_NON_REMOVABLE 0x0001
+#define PROF_REMOVABLE 0x0002
+#define PROF_MAGNETO_OPTICAL 0x0003
+#define PROF_OPTICAL_WO 0x0004
+#define PROF_OPTICAL_ASMO 0x0005
+#define PROF_CDROM 0x0008
+#define PROF_CDR 0x0009
+#define PROF_CDRW 0x000a
+#define PROF_DVDROM 0x0010
+#define PROF_DVDR 0x0011
+#define PROF_DVDRAM 0x0012
+#define PROF_DVDRW_REST 0x0013
+#define PROF_DVDRW_SEQ 0x0014
+#define PROF_DVDRW 0x001a
+#define PROF_DDCD_ROM 0x0020
+#define PROF_DDCD_R 0x0021
+#define PROF_DDCD_RW 0x0022
+#define PROF_NON_CONFORMING 0xffff
+
+struct get_configuration {
+ uchar_t len[4];
+ uchar_t reserved[2];
+ uchar_t curprof[2];
+ struct conf_feature feature;
+};
+
+struct capabilities {
+#if defined(_BIT_FIELDS_LTOH)
+ uchar_t pagecode : 6;
+ uchar_t resv1 : 1;
+ uchar_t ps : 1;
+#else
+ uchar_t ps : 1;
+ uchar_t resv1 : 1;
+ uchar_t pagecode : 6;
+#endif /* _BIT_FIELDS_LTOH */
+ uchar_t pagelen;
+#if defined(_BIT_FIELDS_LTOH)
+ /* read capabilities */
+ uchar_t cdr_read : 1;
+ uchar_t cdrw_read : 1;
+ uchar_t method2 : 1;
+ uchar_t dvdrom_read : 1;
+ uchar_t dvdr_read : 1;
+ uchar_t dvdram_read : 1;
+ uchar_t resv2 : 2;
+#else
+ uchar_t resv2 : 2;
+ uchar_t dvdram_read : 1;
+ uchar_t dvdr_read : 1;
+ uchar_t dvdrom_read : 1;
+ uchar_t method2 : 1;
+ uchar_t cdrw_read : 1;
+ uchar_t cdr_read : 1;
+#endif /* _BIT_FIELDS_LTOH */
+#if defined(_BIT_FIELDS_LTOH)
+ /* write capabilities */
+ uchar_t cdr_write : 1;
+ uchar_t cdrw_write : 1;
+ uchar_t testwrite : 1;
+ uchar_t resv3 : 1;
+ uchar_t dvdr_write : 1;
+ uchar_t dvdram_write : 1;
+ uchar_t resv4 : 2;
+#else
+ /* write capabilities */
+ uchar_t resv4 : 2;
+ uchar_t dvdram_write : 1;
+ uchar_t dvdr_write : 1;
+ uchar_t resv3 : 1;
+ uchar_t testwrite : 1;
+ uchar_t cdrw_write : 1;
+ uchar_t cdr_write : 1;
+#endif /* _BIT_FIELDS_LTOH */
+ uchar_t misc0;
+ uchar_t misc1;
+ uchar_t misc2;
+ uchar_t misc3;
+ uchar_t obsolete0[2];
+ uchar_t numvlevels[2];
+ uchar_t bufsize[2];
+ uchar_t obsolete1[4];
+ uchar_t resv5;
+ uchar_t misc4;
+ uchar_t obsolete2;
+ uchar_t copymgt[2];
+ /* there is more to this page, but nothing we care about */
+};
+
+struct mode_header_g2 {
+ uchar_t modelen[2];
+ uchar_t obsolete;
+ uchar_t reserved[3];
+ uchar_t desclen[2];
+};
+
+/*
+ * Mode sense/select page header information
+ */
+struct scsi_ms_header {
+ struct mode_header mode_header;
+ struct block_descriptor block_descriptor;
+};
+
+#define MODESENSE_PAGE_LEN(p) (((int)((struct mode_page *)p)->length) + \
+ sizeof (struct mode_page))
+
+#define MODE_SENSE_PC_CURRENT (0 << 6)
+#define MODE_SENSE_PC_DEFAULT (2 << 6)
+#define MODE_SENSE_PC_SAVED (3 << 6)
+
+#define MAX_MODE_SENSE_SIZE 255
+#define IMPOSSIBLE_SCSI_STATUS 0xff
+
+/*
+ * ********** end of uscsi stuff ************
+ */
+
+static descriptor_t **apply_filter(descriptor_t **drives, int filter[],
+ int *errp);
+static int check_atapi(int fd);
+static int conv_drive_type(uint_t drive_type);
+static uint64_t convnum(uchar_t *nptr, int len);
+static void fill_command_g1(struct uscsi_cmd *cmd,
+ union scsi_cdb *cdb, caddr_t buff, int blen);
+static void fill_general_page_cdb_g1(union scsi_cdb *cdb,
+ int command, int lun, uchar_t c0, uchar_t c1);
+static void fill_mode_page_cdb(union scsi_cdb *cdb, int page);
+static descriptor_t **get_assoc_alias(disk_t *diskp, int *errp);
+static descriptor_t **get_assoc_controllers(descriptor_t *dp, int *errp);
+static descriptor_t **get_assoc_paths(descriptor_t *dp, int *errp);
+static int get_attrs(disk_t *diskp, int fd, char *opath,
+ nvlist_t *nvp);
+static int get_cdrom_drvtype(int fd);
+static int get_disk_kstats(kstat_ctl_t *kc, char *diskname,
+ char *classname, nvlist_t *stats);
+static void get_drive_type(disk_t *dp, int fd);
+static int get_err_kstats(kstat_ctl_t *kc, char *diskname,
+ nvlist_t *stats);
+static int get_io_kstats(kstat_ctl_t *kc, char *diskname,
+ nvlist_t *stats);
+static int get_kstat_vals(kstat_t *ksp, nvlist_t *stats);
+static char *get_err_attr_name(char *kstat_name);
+static int get_rpm(disk_t *dp, int fd);
+static int update_stat64(nvlist_t *stats, char *attr,
+ uint64_t value);
+static int update_stat32(nvlist_t *stats, char *attr,
+ uint32_t value);
+static int uscsi_mode_sense(int fd, int page_code,
+ int page_control, caddr_t page_data, int page_size,
+ struct scsi_ms_header *header);
+
+descriptor_t **
+drive_get_assoc_descriptors(descriptor_t *dp, dm_desc_type_t type,
+ int *errp)
+{
+ switch (type) {
+ case DM_CONTROLLER:
+ return (get_assoc_controllers(dp, errp));
+ case DM_PATH:
+ return (get_assoc_paths(dp, errp));
+ case DM_ALIAS:
+ return (get_assoc_alias(dp->p.disk, errp));
+ case DM_MEDIA:
+ return (media_get_assocs(dp, errp));
+ }
+
+ *errp = EINVAL;
+ return (NULL);
+}
+
+/*
+ * Get the drive descriptors for the given media/alias/devpath.
+ */
+descriptor_t **
+drive_get_assocs(descriptor_t *desc, int *errp)
+{
+ descriptor_t **drives;
+
+ /* at most one drive is associated with these descriptors */
+
+ drives = (descriptor_t **)calloc(2, sizeof (descriptor_t *));
+ if (drives == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ drives[0] = cache_get_desc(DM_DRIVE, desc->p.disk, NULL, NULL, errp);
+ if (*errp != 0) {
+ cache_free_descriptors(drives);
+ return (NULL);
+ }
+
+ drives[1] = NULL;
+
+ return (drives);
+}
+
+nvlist_t *
+drive_get_attributes(descriptor_t *dp, int *errp)
+{
+ nvlist_t *attrs = NULL;
+ int fd;
+ char opath[MAXPATHLEN];
+
+ if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ opath[0] = 0;
+ fd = drive_open_disk(dp->p.disk, opath, sizeof (opath));
+
+ if ((*errp = get_attrs(dp->p.disk, fd, opath, attrs)) != 0) {
+ nvlist_free(attrs);
+ attrs = NULL;
+ }
+
+ if (fd >= 0) {
+ (void) close(fd);
+ }
+
+ return (attrs);
+}
+
+/*
+ * Check if we have the drive in our list, based upon the device id.
+ * We got the device id from the dev tree walk. This is encoded
+ * using devid_str_encode(3DEVID). In order to check the device ids we need
+ * to use the devid_compare(3DEVID) function, so we need to decode the
+ * string representation of the device id.
+ */
+descriptor_t *
+drive_get_descriptor_by_name(char *name, int *errp)
+{
+ ddi_devid_t devid;
+ descriptor_t **drives;
+ descriptor_t *drive = NULL;
+ int i;
+
+ if (name == NULL || devid_str_decode(name, &devid, NULL) != 0) {
+ *errp = EINVAL;
+ return (NULL);
+ }
+
+ drives = cache_get_descriptors(DM_DRIVE, errp);
+ if (*errp != 0) {
+ devid_free(devid);
+ return (NULL);
+ }
+
+ /*
+ * We have to loop through all of them, freeing the ones we don't
+ * want. Once drive is set, we don't need to compare any more.
+ */
+ for (i = 0; drives[i]; i++) {
+ if (drive == NULL && drives[i]->p.disk->devid != NULL &&
+ devid_compare(devid, drives[i]->p.disk->devid) == 0) {
+ drive = drives[i];
+
+ } else {
+ /* clean up the unused descriptor */
+ cache_free_descriptor(drives[i]);
+ }
+ }
+ free(drives);
+ devid_free(devid);
+
+ if (drive == NULL) {
+ *errp = ENODEV;
+ }
+
+ return (drive);
+}
+
+descriptor_t **
+drive_get_descriptors(int filter[], int *errp)
+{
+ descriptor_t **drives;
+
+ drives = cache_get_descriptors(DM_DRIVE, errp);
+ if (*errp != 0) {
+ return (NULL);
+ }
+
+ if (filter != NULL && filter[0] != DM_FILTER_END) {
+ descriptor_t **found;
+ found = apply_filter(drives, filter, errp);
+ if (*errp != 0) {
+ drives = NULL;
+ } else {
+ drives = found;
+ }
+ }
+
+ return (drives);
+}
+
+char *
+drive_get_name(descriptor_t *dp)
+{
+ return (dp->p.disk->device_id);
+}
+
+nvlist_t *
+drive_get_stats(descriptor_t *dp, int stat_type, int *errp)
+{
+ disk_t *diskp;
+ nvlist_t *stats;
+
+ diskp = dp->p.disk;
+
+ if (nvlist_alloc(&stats, NVATTRS, 0) != 0) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ if (stat_type == DM_DRV_STAT_PERFORMANCE ||
+ stat_type == DM_DRV_STAT_DIAGNOSTIC) {
+
+ alias_t *ap;
+ kstat_ctl_t *kc;
+
+ ap = diskp->aliases;
+ if (ap == NULL || ap->kstat_name == NULL) {
+ nvlist_free(stats);
+ *errp = EACCES;
+ return (NULL);
+ }
+
+ if ((kc = kstat_open()) == NULL) {
+ nvlist_free(stats);
+ *errp = EACCES;
+ return (NULL);
+ }
+
+ while (ap != NULL) {
+ int status;
+
+ if (ap->kstat_name == NULL) {
+ continue;
+ }
+
+ if (stat_type == DM_DRV_STAT_PERFORMANCE) {
+ status = get_io_kstats(kc, ap->kstat_name, stats);
+ } else {
+ status = get_err_kstats(kc, ap->kstat_name, stats);
+ }
+
+ if (status != 0) {
+ nvlist_free(stats);
+ (void) kstat_close(kc);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ ap = ap->next;
+ }
+
+ (void) kstat_close(kc);
+
+ *errp = 0;
+ return (stats);
+ }
+
+ if (stat_type == DM_DRV_STAT_TEMPERATURE) {
+ int fd;
+
+ if ((fd = drive_open_disk(diskp, NULL, 0)) >= 0) {
+ struct dk_temperature temp;
+
+ if (ioctl(fd, DKIOCGTEMPERATURE, &temp) >= 0) {
+ if (nvlist_add_uint32(stats, DM_TEMPERATURE,
+ temp.dkt_cur_temp) != 0) {
+ *errp = ENOMEM;
+ nvlist_free(stats);
+ return (NULL);
+ }
+ } else {
+ *errp = errno;
+ nvlist_free(stats);
+ return (NULL);
+ }
+ (void) close(fd);
+ } else {
+ *errp = errno;
+ nvlist_free(stats);
+ return (NULL);
+ }
+
+ *errp = 0;
+ return (stats);
+ }
+
+ nvlist_free(stats);
+ *errp = EINVAL;
+ return (NULL);
+}
+
+int
+drive_make_descriptors()
+{
+ int error;
+ disk_t *dp;
+
+ dp = cache_get_disklist();
+ while (dp != NULL) {
+ cache_load_desc(DM_DRIVE, dp, NULL, NULL, &error);
+ if (error != 0) {
+ return (error);
+ }
+ dp = dp->next;
+ }
+
+ return (0);
+}
+
+/*
+ * This function opens the disk generically (any slice).
+ *
+ * Opening the disk could fail because the disk is managed by the volume
+ * manager. Handle this if that is the case. Note that the media APIs don't
+ * always return a device. If the media has slices (e.g. a solaris install
+ * CD-ROM) then media_findname(volname) returns a directory with per slice
+ * devices underneath. We need to open one of those devices in this case.
+ */
+int
+drive_open_disk(disk_t *diskp, char *opath, int len)
+{
+ char rmmedia_devpath[MAXPATHLEN];
+
+ if (diskp->removable && media_get_volm_path(diskp, rmmedia_devpath,
+ sizeof (rmmedia_devpath))) {
+
+ int fd;
+ struct stat buf;
+
+ if (rmmedia_devpath[0] == 0) {
+ /* removable but no media */
+ return (-1);
+ }
+
+ if ((fd = open(rmmedia_devpath, O_RDONLY|O_NDELAY)) < 0) {
+ return (-1);
+ }
+
+ if (fstat(fd, &buf) != 0) {
+ (void) close(fd);
+ return (-1);
+ }
+
+ if (buf.st_mode & S_IFCHR) {
+ /* opened, is device, so done */
+ if (opath != NULL) {
+ (void) strlcpy(opath, rmmedia_devpath, len);
+ }
+ return (fd);
+
+ } else if (buf.st_mode & S_IFDIR) {
+ /* disk w/ slices so handle the directory */
+ DIR *dirp;
+ struct dirent *dentp;
+ int dfd;
+#ifdef _LP64
+ struct dirent *result;
+#endif
+
+ /* each device file in the dir represents a slice */
+
+ if ((dirp = fdopendir(fd)) == NULL) {
+ (void) close(fd);
+ return (-1);
+ }
+
+ if ((dentp = (struct dirent *)malloc(sizeof (struct dirent) +
+ _PC_NAME_MAX + 1)) == NULL) {
+ /* out of memory */
+ (void) close(fd);
+ return (-1);
+ }
+#ifdef _LP64
+ while (readdir_r(dirp, dentp, &result) != NULL) {
+#else
+ while (readdir_r(dirp, dentp) != NULL) {
+#endif
+ char slice_path[MAXPATHLEN];
+
+ if (libdiskmgt_str_eq(".", dentp->d_name) ||
+ libdiskmgt_str_eq("..", dentp->d_name)) {
+ continue;
+ }
+
+ (void) snprintf(slice_path, sizeof (slice_path), "%s/%s",
+ rmmedia_devpath, dentp->d_name);
+
+ if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) < 0) {
+ continue;
+ }
+
+ if (fstat(dfd, &buf) == 0 && (buf.st_mode & S_IFCHR)) {
+ /* opened, is device, so done */
+ free(dentp);
+ (void) close(fd);
+ if (opath != NULL) {
+ (void) strlcpy(opath, slice_path, len);
+ }
+ return (dfd);
+ }
+
+ /* not a device, keep looking */
+ (void) close(dfd);
+ }
+
+ /* did not find a device under the rmmedia_path */
+ free(dentp);
+ (void) close(fd);
+ }
+
+ /* didn't find a device under volume management control */
+ return (-1);
+ }
+
+ /*
+ * Not removable media under volume management control so just open the
+ * first devpath.
+ */
+ if (diskp->aliases != NULL && diskp->aliases->devpaths != NULL) {
+ if (opath != NULL) {
+ (void) strlcpy(opath, diskp->aliases->devpaths->devpath, len);
+ }
+ return (open(diskp->aliases->devpaths->devpath, O_RDONLY|O_NDELAY));
+ }
+
+ return (-1);
+}
+
+static descriptor_t **
+apply_filter(descriptor_t **drives, int filter[], int *errp)
+{
+ int i;
+ descriptor_t **found;
+ int cnt;
+ int pos;
+
+ /* count the number of drives in the snapshot */
+ for (cnt = 0; drives[cnt]; cnt++);
+
+ found = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
+ if (found == NULL) {
+ *errp = ENOMEM;
+ cache_free_descriptors(drives);
+ return (NULL);
+ }
+
+ pos = 0;
+ for (i = 0; drives[i]; i++) {
+ int j;
+ int match;
+
+ /* Make sure the drive type is set */
+ get_drive_type(drives[i]->p.disk, -1);
+
+ match = 0;
+ for (j = 0; filter[j] != DM_FILTER_END; j++) {
+ if (drives[i]->p.disk->drv_type == filter[j]) {
+ found[pos++] = drives[i];
+ match = 1;
+ break;
+ }
+ }
+
+ if (!match) {
+ cache_free_descriptor(drives[i]);
+ }
+ }
+ found[pos] = NULL;
+ free(drives);
+
+ *errp = 0;
+ return (found);
+}
+
+static int
+conv_drive_type(uint_t drive_type)
+{
+ switch (drive_type) {
+ case DK_UNKNOWN:
+ return (DM_DT_UNKNOWN);
+ case DK_MO_ERASABLE:
+ return (DM_DT_MO_ERASABLE);
+ case DK_MO_WRITEONCE:
+ return (DM_DT_MO_WRITEONCE);
+ case DK_AS_MO:
+ return (DM_DT_AS_MO);
+ case DK_CDROM:
+ return (DM_DT_CDROM);
+ case DK_CDR:
+ return (DM_DT_CDR);
+ case DK_CDRW:
+ return (DM_DT_CDRW);
+ case DK_DVDROM:
+ return (DM_DT_DVDROM);
+ case DK_DVDR:
+ return (DM_DT_DVDR);
+ case DK_DVDRAM:
+ return (DM_DT_DVDRAM);
+ case DK_FIXED_DISK:
+ return (DM_DT_FIXED);
+ case DK_FLOPPY:
+ return (DM_DT_FLOPPY);
+ case DK_ZIP:
+ return (DM_DT_ZIP);
+ case DK_JAZ:
+ return (DM_DT_JAZ);
+ default:
+ return (DM_DT_UNKNOWN);
+ }
+}
+
+static descriptor_t **
+get_assoc_alias(disk_t *diskp, int *errp)
+{
+ alias_t *aliasp;
+ uint_t cnt;
+ descriptor_t **out_array;
+ int pos;
+
+ *errp = 0;
+
+ aliasp = diskp->aliases;
+ cnt = 0;
+
+ while (aliasp != NULL) {
+ if (aliasp->alias != NULL) {
+ cnt++;
+ }
+ aliasp = aliasp->next;
+ }
+
+ /* set up the new array */
+ out_array = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t));
+ if (out_array == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ aliasp = diskp->aliases;
+ pos = 0;
+ while (aliasp != NULL) {
+ if (aliasp->alias != NULL) {
+ out_array[pos++] = cache_get_desc(DM_ALIAS, diskp,
+ aliasp->alias, NULL, errp);
+ if (*errp != 0) {
+ cache_free_descriptors(out_array);
+ return (NULL);
+ }
+ }
+
+ aliasp = aliasp->next;
+ }
+
+ out_array[pos] = NULL;
+
+ return (out_array);
+}
+
+static descriptor_t **
+get_assoc_controllers(descriptor_t *dp, int *errp)
+{
+ disk_t *diskp;
+ int cnt;
+ descriptor_t **controllers;
+ int i;
+
+ diskp = dp->p.disk;
+
+ /* Count how many we have. */
+ for (cnt = 0; diskp->controllers[cnt]; cnt++);
+
+ /* make the snapshot */
+ controllers = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
+ if (controllers == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ for (i = 0; diskp->controllers[i]; i++) {
+ controllers[i] = cache_get_desc(DM_CONTROLLER,
+ diskp->controllers[i], NULL, NULL, errp);
+ if (*errp != 0) {
+ cache_free_descriptors(controllers);
+ return (NULL);
+ }
+ }
+
+ controllers[i] = NULL;
+
+ *errp = 0;
+ return (controllers);
+}
+
+static descriptor_t **
+get_assoc_paths(descriptor_t *dp, int *errp)
+{
+ path_t **pp;
+ int cnt;
+ descriptor_t **paths;
+ int i;
+
+ pp = dp->p.disk->paths;
+
+ /* Count how many we have. */
+ cnt = 0;
+ if (pp != NULL) {
+ for (; pp[cnt]; cnt++);
+ }
+
+ /* make the snapshot */
+ paths = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
+ if (paths == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ /*
+ * We fill in the name field of the descriptor with the device_id
+ * when we deal with path descriptors originating from a drive.
+ * In that way we can use the device id within the path code to
+ * lookup the path state for this drive.
+ */
+ for (i = 0; i < cnt; i++) {
+ paths[i] = cache_get_desc(DM_PATH, pp[i], dp->p.disk->device_id,
+ NULL, errp);
+ if (*errp != 0) {
+ cache_free_descriptors(paths);
+ return (NULL);
+ }
+ }
+
+ paths[i] = NULL;
+
+ *errp = 0;
+ return (paths);
+}
+
+static int
+get_attrs(disk_t *diskp, int fd, char *opath, nvlist_t *attrs)
+{
+ if (diskp->removable) {
+ struct dk_minfo minfo;
+
+ if (nvlist_add_boolean(attrs, DM_REMOVABLE) != 0) {
+ return (ENOMEM);
+ }
+
+ /* Make sure media is inserted and spun up. */
+ if (fd >= 0 && media_read_info(fd, &minfo)) {
+ if (nvlist_add_boolean(attrs, DM_LOADED) != 0) {
+ return (ENOMEM);
+ }
+ }
+
+ /* can't tell diff between dead & no media on removable drives */
+ if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_UP) != 0) {
+ return (ENOMEM);
+ }
+
+ get_drive_type(diskp, fd);
+
+ } else {
+ struct dk_minfo minfo;
+
+ /* check if the fixed drive is up or not */
+ if (fd >= 0 && media_read_info(fd, &minfo)) {
+ if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_UP) != 0) {
+ return (ENOMEM);
+ }
+ } else {
+ if (nvlist_add_uint32(attrs, DM_STATUS, DM_DISK_DOWN) != 0) {
+ return (ENOMEM);
+ }
+ }
+
+ get_drive_type(diskp, fd);
+ }
+
+ if (nvlist_add_uint32(attrs, DM_DRVTYPE, diskp->drv_type) != 0) {
+ return (ENOMEM);
+ }
+
+ if (diskp->product_id != NULL) {
+ if (nvlist_add_string(attrs, DM_PRODUCT_ID, diskp->product_id)
+ != 0) {
+ return (ENOMEM);
+ }
+ }
+ if (diskp->vendor_id != NULL) {
+ if (nvlist_add_string(attrs, DM_VENDOR_ID, diskp->vendor_id) != 0) {
+ return (ENOMEM);
+ }
+ }
+
+ if (diskp->sync_speed != -1) {
+ if (nvlist_add_uint32(attrs, DM_SYNC_SPEED, diskp->sync_speed)
+ != 0) {
+ return (ENOMEM);
+ }
+ }
+
+ if (diskp->wide == 1) {
+ if (nvlist_add_boolean(attrs, DM_WIDE) != 0) {
+ return (ENOMEM);
+ }
+ }
+
+ if (diskp->rpm == 0) {
+ diskp->rpm = get_rpm(diskp, fd);
+ }
+
+ if (diskp->rpm > 0) {
+ if (nvlist_add_uint32(attrs, DM_RPM, diskp->rpm) != 0) {
+ return (ENOMEM);
+ }
+ }
+
+ if (diskp->aliases != NULL && diskp->aliases->cluster) {
+ if (nvlist_add_boolean(attrs, DM_CLUSTERED) != 0) {
+ return (ENOMEM);
+ }
+ }
+
+ if (strlen(opath) > 0) {
+ if (nvlist_add_string(attrs, DM_OPATH, opath) != 0) {
+ return (ENOMEM);
+ }
+ }
+
+ return (0);
+}
+
+static int
+get_disk_kstats(kstat_ctl_t *kc, char *diskname, char *classname,
+ nvlist_t *stats)
+{
+ kstat_t *ksp;
+ size_t class_len;
+ int err = 0;
+
+ class_len = strlen(classname);
+ for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
+ if (strncmp(ksp->ks_class, classname, class_len) == 0) {
+ char kstat_name[KSTAT_STRLEN];
+ char *dname = kstat_name;
+ char *ename = ksp->ks_name;
+
+ /* names are format: "sd0,err" - copy chars up to comma */
+ while (*ename && *ename != ',') {
+ *dname++ = *ename++;
+ }
+ *dname = NULL;
+
+ if (libdiskmgt_str_eq(diskname, kstat_name)) {
+ (void) kstat_read(kc, ksp, NULL);
+ err = get_kstat_vals(ksp, stats);
+ break;
+ }
+ }
+ }
+
+ return (err);
+}
+
+/*
+ * Getting the drive type depends on if the dev tree walk indicated that the
+ * drive was a CD-ROM or not. The kernal lumps all of the removable multi-media
+ * drives (e.g. CD, DVD, MO, etc.) together as CD-ROMS, so we need to use
+ * a uscsi cmd to check the drive type.
+ */
+static void
+get_drive_type(disk_t *dp, int fd)
+{
+ if (dp->drv_type == DM_DT_UNKNOWN) {
+ int opened_here = 0;
+
+ /* We may have already opened the device. */
+ if (fd < 0) {
+ fd = drive_open_disk(dp, NULL, 0);
+ opened_here = 1;
+ }
+
+ if (fd >= 0) {
+ if (dp->cd_rom) {
+ /* use uscsi to determine drive type */
+ dp->drv_type = get_cdrom_drvtype(fd);
+
+ /* if uscsi fails, just call it a cd-rom */
+ if (dp->drv_type == DM_DT_UNKNOWN) {
+ dp->drv_type = DM_DT_CDROM;
+ }
+
+ } else {
+ struct dk_minfo minfo;
+
+ if (media_read_info(fd, &minfo)) {
+ dp->drv_type = conv_drive_type(minfo.dki_media_type);
+ }
+ }
+
+ if (opened_here) {
+ (void) close(fd);
+ }
+
+ } else {
+ /* couldn't open */
+ if (dp->cd_rom) {
+ dp->drv_type = DM_DT_CDROM;
+ }
+ }
+ }
+}
+
+static char *
+get_err_attr_name(char *kstat_name)
+{
+ int i;
+
+ for (i = 0; kstat_err_names[i] != NULL; i++) {
+ if (libdiskmgt_str_eq(kstat_name, kstat_err_names[i])) {
+ return (err_attr_names[i]);
+ }
+ }
+
+ return (NULL);
+}
+
+static int
+get_err_kstats(kstat_ctl_t *kc, char *diskname, nvlist_t *stats)
+{
+ return (get_disk_kstats(kc, diskname, KSTAT_CLASS_ERROR, stats));
+}
+
+static int
+get_io_kstats(kstat_ctl_t *kc, char *diskname, nvlist_t *stats)
+{
+ return (get_disk_kstats(kc, diskname, KSTAT_CLASS_DISK, stats));
+}
+
+static int
+get_kstat_vals(kstat_t *ksp, nvlist_t *stats)
+{
+ if (ksp->ks_type == KSTAT_TYPE_IO) {
+ kstat_io_t *kiop;
+
+ kiop = KSTAT_IO_PTR(ksp);
+
+ /* see sys/kstat.h kstat_io_t struct for more fields */
+
+ if (update_stat64(stats, DM_NBYTESREAD, kiop->nread) != 0) {
+ return (ENOMEM);
+ }
+ if (update_stat64(stats, DM_NBYTESWRITTEN, kiop->nwritten) != 0) {
+ return (ENOMEM);
+ }
+ if (update_stat64(stats, DM_NREADOPS, kiop->reads) != 0) {
+ return (ENOMEM);
+ }
+ if (update_stat64(stats, DM_NWRITEOPS, kiop->writes) != 0) {
+ return (ENOMEM);
+ }
+
+ } else if (ksp->ks_type == KSTAT_TYPE_NAMED) {
+ kstat_named_t *knp;
+ int i;
+
+ knp = KSTAT_NAMED_PTR(ksp);
+ for (i = 0; i < ksp->ks_ndata; i++) {
+ char *attr_name;
+
+ if (knp[i].name[0] == 0)
+ continue;
+
+ if ((attr_name = get_err_attr_name(knp[i].name)) == NULL) {
+ continue;
+
+ }
+
+ switch (knp[i].data_type) {
+ case KSTAT_DATA_UINT32:
+ if (update_stat32(stats, attr_name, knp[i].value.ui32)
+ != 0) {
+ return (ENOMEM);
+ }
+ break;
+
+ default:
+ /* Right now all of the error types are uint32 */
+ break;
+ }
+ }
+ }
+ return (0);
+}
+
+static int
+update_stat32(nvlist_t *stats, char *attr, uint32_t value)
+{
+ int32_t currval;
+
+ if (nvlist_lookup_int32(stats, attr, &currval) == 0) {
+ value += currval;
+ }
+
+ return (nvlist_add_uint32(stats, attr, value));
+}
+
+/*
+ * There can be more than one kstat value when we have multi-path drives
+ * that are not under mpxio (since there is more than one kstat name for
+ * the drive in this case). So, we may have merge all of the kstat values
+ * to give an accurate set of stats for the drive.
+ */
+static int
+update_stat64(nvlist_t *stats, char *attr, uint64_t value)
+{
+ int64_t currval;
+
+ if (nvlist_lookup_int64(stats, attr, &currval) == 0) {
+ value += currval;
+ }
+ return (nvlist_add_uint64(stats, attr, value));
+}
+
+/*
+ * uscsi function to get the rpm of the drive
+ */
+static int
+get_rpm(disk_t *dp, int fd)
+{
+ int opened_here = 0;
+ int rpm = -1;
+
+ /* We may have already opened the device. */
+ if (fd < 0) {
+ fd = drive_open_disk(dp, NULL, 0);
+ opened_here = 1;
+ }
+
+ if (fd >= 0) {
+ int status;
+ struct mode_geometry *page4;
+ struct scsi_ms_header header;
+ union {
+ struct mode_geometry page4;
+ char rawbuf[MAX_MODE_SENSE_SIZE];
+ } u_page4;
+
+ page4 = &u_page4.page4;
+ (void) memset(&u_page4, 0, sizeof (u_page4));
+
+ status = uscsi_mode_sense(fd, DAD_MODE_GEOMETRY,
+ MODE_SENSE_PC_DEFAULT, (caddr_t)page4, MAX_MODE_SENSE_SIZE,
+ &header);
+
+ if (status) {
+ status = uscsi_mode_sense(fd, DAD_MODE_GEOMETRY,
+ MODE_SENSE_PC_SAVED, (caddr_t)page4, MAX_MODE_SENSE_SIZE,
+ &header);
+ }
+
+ if (status) {
+ status = uscsi_mode_sense(fd, DAD_MODE_GEOMETRY,
+ MODE_SENSE_PC_CURRENT, (caddr_t)page4, MAX_MODE_SENSE_SIZE,
+ &header);
+ }
+
+ if (!status) {
+#ifdef _LITTLE_ENDIAN
+ page4->rpm = ntohs(page4->rpm);
+#endif /* _LITTLE_ENDIAN */
+
+ rpm = page4->rpm;
+ }
+
+ if (opened_here) {
+ (void) close(fd);
+ }
+ }
+
+ return (rpm);
+}
+
+/*
+ * ******** the rest of this is uscsi stuff for the drv type ********
+ */
+
+/*
+ * We try a get_configuration uscsi cmd. If that fails, try a
+ * atapi_capabilities cmd. If both fail then this is an older CD-ROM.
+ */
+static int
+get_cdrom_drvtype(int fd)
+{
+ union scsi_cdb cdb;
+ struct uscsi_cmd cmd;
+ uchar_t buff[SCSIBUFLEN];
+
+ fill_general_page_cdb_g1(&cdb, SCMD_GET_CONFIGURATION, 0,
+ b0(sizeof (buff)), b1(sizeof (buff)));
+ fill_command_g1(&cmd, &cdb, (caddr_t)buff, sizeof (buff));
+
+ if (ioctl(fd, USCSICMD, &cmd) >= 0) {
+ struct get_configuration *config;
+ struct conf_feature *feature;
+ int flen;
+
+ /* The first profile is the preferred one for the drive. */
+ config = (struct get_configuration *)buff;
+ feature = &config->feature;
+ flen = feature->len / sizeof (struct profile_list);
+ if (flen > 0) {
+ int prof_num;
+
+ prof_num = (int)convnum(feature->features.plist[0].profile, 2);
+
+ if (dm_debug > 1) {
+ (void) fprintf(stderr, "INFO: uscsi get_configuration %d\n",
+ prof_num);
+ }
+
+ switch (prof_num) {
+ case PROF_MAGNETO_OPTICAL:
+ return (DM_DT_MO_ERASABLE);
+ case PROF_OPTICAL_WO:
+ return (DM_DT_MO_WRITEONCE);
+ case PROF_OPTICAL_ASMO:
+ return (DM_DT_AS_MO);
+ case PROF_CDROM:
+ return (DM_DT_CDROM);
+ case PROF_CDR:
+ return (DM_DT_CDR);
+ case PROF_CDRW:
+ return (DM_DT_CDRW);
+ case PROF_DVDROM:
+ return (DM_DT_DVDROM);
+ case PROF_DVDRAM:
+ return (DM_DT_DVDRAM);
+ case PROF_DVDRW_REST:
+ return (DM_DT_DVDRW);
+ case PROF_DVDRW_SEQ:
+ return (DM_DT_DVDRW);
+ case PROF_DVDRW:
+ return (DM_DT_DVDRW);
+ case PROF_DDCD_ROM:
+ return (DM_DT_DDCDROM);
+ case PROF_DDCD_R:
+ return (DM_DT_DDCDR);
+ case PROF_DDCD_RW:
+ return (DM_DT_DDCDRW);
+ }
+ }
+ }
+
+ /* see if the atapi capabilities give anything */
+ return (check_atapi(fd));
+}
+
+static int
+check_atapi(int fd)
+{
+ union scsi_cdb cdb;
+ struct uscsi_cmd cmd;
+ uchar_t buff[SCSIBUFLEN];
+
+ fill_mode_page_cdb(&cdb, ATAPI_CAPABILITIES);
+ fill_command_g1(&cmd, &cdb, (caddr_t)buff, sizeof (buff));
+
+ if (ioctl(fd, USCSICMD, &cmd) >= 0) {
+ int bdesclen;
+ struct capabilities *cap;
+ struct mode_header_g2 *mode;
+
+ mode = (struct mode_header_g2 *)buff;
+
+ bdesclen = (int)convnum(mode->desclen, 2);
+ cap = (struct capabilities *)
+ &buff[sizeof (struct mode_header_g2) + bdesclen];
+
+ if (dm_debug > 1) {
+ (void) fprintf(stderr, "INFO: uscsi atapi capabilities\n");
+ }
+
+ /* These are in order of how we want to report the drv type. */
+ if (cap->dvdram_write) {
+ return (DM_DT_DVDRAM);
+ }
+ if (cap->dvdr_write) {
+ return (DM_DT_DVDR);
+ }
+ if (cap->dvdrom_read) {
+ return (DM_DT_DVDROM);
+ }
+ if (cap->cdrw_write) {
+ return (DM_DT_CDRW);
+ }
+ if (cap->cdr_write) {
+ return (DM_DT_CDR);
+ }
+ if (cap->cdr_read) {
+ return (DM_DT_CDROM);
+ }
+ }
+
+ /* everything failed, so this is an older CD-ROM */
+ if (dm_debug > 1) {
+ (void) fprintf(stderr, "INFO: uscsi failed\n");
+ }
+
+ return (DM_DT_CDROM);
+}
+
+static uint64_t
+convnum(uchar_t *nptr, int len)
+{
+ uint64_t value;
+
+ for (value = 0; len > 0; len--, nptr++)
+ value = (value << 8) | *nptr;
+ return (value);
+}
+
+static void
+fill_command_g1(struct uscsi_cmd *cmd, union scsi_cdb *cdb,
+ caddr_t buff, int blen)
+{
+ bzero((caddr_t)cmd, sizeof (struct uscsi_cmd));
+ bzero(buff, blen);
+
+ cmd->uscsi_cdb = (caddr_t)cdb;
+ cmd->uscsi_cdblen = CDB_GROUP1;
+
+ cmd->uscsi_bufaddr = buff;
+ cmd->uscsi_buflen = blen;
+
+ cmd->uscsi_flags = USCSI_DIAGNOSE|USCSI_ISOLATE|USCSI_READ;
+}
+
+static void
+fill_general_page_cdb_g1(union scsi_cdb *cdb, int command, int lun,
+ uchar_t c0, uchar_t c1)
+{
+ bzero((caddr_t)cdb, sizeof (union scsi_cdb));
+ cdb->scc_cmd = command;
+ cdb->scc_lun = lun;
+ cdb->g1_count0 = c0; /* max length for page */
+ cdb->g1_count1 = c1; /* max length for page */
+}
+
+static void
+fill_mode_page_cdb(union scsi_cdb *cdb, int page)
+{
+ /* group 1 mode page */
+ bzero((caddr_t)cdb, sizeof (union scsi_cdb));
+ cdb->scc_cmd = SCMD_MODE_SENSE_G1;
+ cdb->g1_count0 = 0xff; /* max length for mode page */
+ cdb->g1_count1 = 0xff; /* max length for mode page */
+ cdb->g1_addr3 = page;
+}
+
+static int
+uscsi_mode_sense(int fd, int page_code, int page_control, caddr_t page_data,
+ int page_size, struct scsi_ms_header *header)
+{
+ caddr_t mode_sense_buf;
+ struct mode_header *hdr;
+ struct mode_page *pg;
+ int nbytes;
+ struct uscsi_cmd ucmd;
+ union scsi_cdb cdb;
+ int status;
+ int maximum;
+ char rqbuf[255];
+
+ /*
+ * Allocate a buffer for the mode sense headers
+ * and mode sense data itself.
+ */
+ nbytes = sizeof (struct block_descriptor) +
+ sizeof (struct mode_header) + page_size;
+ nbytes = page_size;
+ if ((mode_sense_buf = malloc((uint_t)nbytes)) == NULL) {
+ return (-1);
+ }
+
+ /*
+ * Build and execute the uscsi ioctl
+ */
+ (void) memset(mode_sense_buf, 0, nbytes);
+ (void) memset((char *)&ucmd, 0, sizeof (ucmd));
+ (void) memset((char *)&cdb, 0, sizeof (union scsi_cdb));
+
+ cdb.scc_cmd = SCMD_MODE_SENSE;
+ FORMG0COUNT(&cdb, (uchar_t)nbytes);
+ cdb.cdb_opaque[2] = page_control | page_code;
+ ucmd.uscsi_cdb = (caddr_t)&cdb;
+ ucmd.uscsi_cdblen = CDB_GROUP0;
+ ucmd.uscsi_bufaddr = mode_sense_buf;
+ ucmd.uscsi_buflen = nbytes;
+
+ ucmd.uscsi_flags |= USCSI_SILENT;
+ ucmd.uscsi_flags |= USCSI_READ;
+ ucmd.uscsi_timeout = 30;
+ ucmd.uscsi_flags |= USCSI_RQENABLE;
+ if (ucmd.uscsi_rqbuf == NULL) {
+ ucmd.uscsi_rqbuf = rqbuf;
+ ucmd.uscsi_rqlen = sizeof (rqbuf);
+ ucmd.uscsi_rqresid = sizeof (rqbuf);
+ }
+ ucmd.uscsi_rqstatus = IMPOSSIBLE_SCSI_STATUS;
+
+ status = ioctl(fd, USCSICMD, &ucmd);
+
+ if (status || ucmd.uscsi_status != 0) {
+ free(mode_sense_buf);
+ return (-1);
+ }
+
+ /*
+ * Verify that the returned data looks reasonabled,
+ * find the actual page data, and copy it into the
+ * user's buffer. Copy the mode_header and block_descriptor
+ * into the header structure, which can then be used to
+ * return the same data to the drive when issuing a mode select.
+ */
+ hdr = (struct mode_header *)mode_sense_buf;
+ (void) memset((caddr_t)header, 0, sizeof (struct scsi_ms_header));
+ if (hdr->bdesc_length != sizeof (struct block_descriptor) &&
+ hdr->bdesc_length != 0) {
+ free(mode_sense_buf);
+ return (-1);
+ }
+ (void) memcpy((caddr_t)header, mode_sense_buf,
+ (int) (sizeof (struct mode_header) + hdr->bdesc_length));
+ pg = (struct mode_page *)((ulong_t)mode_sense_buf +
+ sizeof (struct mode_header) + hdr->bdesc_length);
+ if (pg->code != page_code) {
+ free(mode_sense_buf);
+ return (-1);
+ }
+
+ /*
+ * Accept up to "page_size" bytes of mode sense data.
+ * This allows us to accept both CCS and SCSI-2
+ * structures, as long as we request the greater
+ * of the two.
+ */
+ maximum = page_size - sizeof (struct mode_page) - hdr->bdesc_length;
+ if (((int)pg->length) > maximum) {
+ free(mode_sense_buf);
+ return (-1);
+ }
+
+ (void) memcpy(page_data, (caddr_t)pg, MODESENSE_PAGE_LEN(pg));
+
+ free(mode_sense_buf);
+ return (0);
+}
diff --git a/usr/src/lib/libdiskmgt/common/entry.c b/usr/src/lib/libdiskmgt/common/entry.c
new file mode 100644
index 0000000000..e898e30a15
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/entry.c
@@ -0,0 +1,599 @@
+/*
+ * 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 <fcntl.h>
+#include <libdevinfo.h>
+#include <stdio.h>
+#include <sys/sunddi.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+#include "partition.h"
+
+
+extern dm_desc_type_t drive_assoc_types[];
+extern dm_desc_type_t bus_assoc_types[];
+extern dm_desc_type_t controller_assoc_types[];
+extern dm_desc_type_t media_assoc_types[];
+extern dm_desc_type_t slice_assoc_types[];
+extern dm_desc_type_t partition_assoc_types[];
+extern dm_desc_type_t path_assoc_types[];
+extern dm_desc_type_t alias_assoc_types[];
+
+static dm_descriptor_t *ptr_array_to_desc_array(descriptor_t **ptrs, int *errp);
+static descriptor_t **desc_array_to_ptr_array(dm_descriptor_t *da, int *errp);
+
+void
+dm_free_descriptor(dm_descriptor_t desc)
+{
+ descriptor_t *dp;
+
+ if (desc == NULL) {
+ return;
+ }
+ dp = (descriptor_t *)desc;
+
+ cache_wlock();
+ cache_free_descriptor(dp);
+ cache_unlock();
+}
+
+void
+dm_free_descriptors(dm_descriptor_t *desc_list)
+{
+ descriptor_t **dp;
+ int error;
+
+ if (desc_list == NULL) {
+ return;
+ }
+ dp = desc_array_to_ptr_array(desc_list, &error);
+ if (error != 0) {
+ free(desc_list);
+ return;
+ }
+
+ cache_wlock();
+ cache_free_descriptors(dp);
+ cache_unlock();
+}
+
+/*ARGSUSED*/
+void
+dm_free_name(char *name)
+{
+ free(name);
+}
+
+dm_descriptor_t *
+dm_get_associated_descriptors(dm_descriptor_t desc, dm_desc_type_t type,
+ int *errp)
+{
+ descriptor_t **descs = NULL;
+ descriptor_t *dp;
+
+
+ dp = (descriptor_t *)desc;
+
+ cache_wlock();
+
+ if (!cache_is_valid_desc(dp)) {
+ cache_unlock();
+ *errp = EBADF;
+ return (NULL);
+ }
+
+ /* verify that the descriptor is still valid */
+ if (dp->p.generic == NULL) {
+ cache_unlock();
+ *errp = ENODEV;
+ return (NULL);
+ }
+
+ switch (dp->type) {
+ case DM_DRIVE:
+ descs = drive_get_assoc_descriptors(dp, type, errp);
+ break;
+ case DM_BUS:
+ descs = bus_get_assoc_descriptors(dp, type, errp);
+ break;
+ case DM_CONTROLLER:
+ descs = controller_get_assoc_descriptors(dp, type, errp);
+ break;
+ case DM_MEDIA:
+ descs = media_get_assoc_descriptors(dp, type, errp);
+ break;
+ case DM_SLICE:
+ descs = slice_get_assoc_descriptors(dp, type, errp);
+ break;
+ case DM_PARTITION:
+ descs = partition_get_assoc_descriptors(dp, type, errp);
+ break;
+ case DM_PATH:
+ descs = path_get_assoc_descriptors(dp, type, errp);
+ break;
+ case DM_ALIAS:
+ descs = alias_get_assoc_descriptors(dp, type, errp);
+ break;
+ default:
+ *errp = EINVAL;
+ break;
+ }
+
+ cache_unlock();
+
+ return (ptr_array_to_desc_array(descs, errp));
+}
+
+dm_desc_type_t *
+dm_get_associated_types(dm_desc_type_t type)
+{
+ switch (type) {
+ case DM_DRIVE:
+ return (drive_assoc_types);
+ case DM_BUS:
+ return (bus_assoc_types);
+ case DM_CONTROLLER:
+ return (controller_assoc_types);
+ case DM_MEDIA:
+ return (media_assoc_types);
+ case DM_SLICE:
+ return (slice_assoc_types);
+ case DM_PARTITION:
+ return (partition_assoc_types);
+ case DM_PATH:
+ return (path_assoc_types);
+ case DM_ALIAS:
+ return (alias_assoc_types);
+ }
+
+ return (NULL);
+}
+
+nvlist_t *
+dm_get_attributes(dm_descriptor_t desc, int *errp)
+{
+ descriptor_t *dp;
+ nvlist_t *attrs = NULL;
+
+
+ dp = (descriptor_t *)desc;
+
+ cache_rlock();
+
+ if (!cache_is_valid_desc(dp)) {
+ cache_unlock();
+ *errp = EBADF;
+ return (NULL);
+ }
+
+ /* verify that the descriptor is still valid */
+ if (dp->p.generic == NULL) {
+ cache_unlock();
+ *errp = ENODEV;
+ return (NULL);
+ }
+
+ switch (dp->type) {
+ case DM_DRIVE:
+ attrs = drive_get_attributes(dp, errp);
+ break;
+ case DM_BUS:
+ attrs = bus_get_attributes(dp, errp);
+ break;
+ case DM_CONTROLLER:
+ attrs = controller_get_attributes(dp, errp);
+ break;
+ case DM_MEDIA:
+ attrs = media_get_attributes(dp, errp);
+ break;
+ case DM_SLICE:
+ attrs = slice_get_attributes(dp, errp);
+ break;
+ case DM_PARTITION:
+ attrs = partition_get_attributes(dp, errp);
+ break;
+ case DM_PATH:
+ attrs = path_get_attributes(dp, errp);
+ break;
+ case DM_ALIAS:
+ attrs = alias_get_attributes(dp, errp);
+ break;
+ default:
+ *errp = EINVAL;
+ break;
+ }
+
+ cache_unlock();
+
+ return (attrs);
+}
+
+dm_descriptor_t
+dm_get_descriptor_by_name(dm_desc_type_t desc_type, char *name, int *errp)
+{
+ dm_descriptor_t desc = NULL;
+
+
+ cache_wlock();
+
+ switch (desc_type) {
+ case DM_DRIVE:
+ desc = (uintptr_t)drive_get_descriptor_by_name(name, errp);
+ break;
+ case DM_BUS:
+ desc = (uintptr_t)bus_get_descriptor_by_name(name, errp);
+ break;
+ case DM_CONTROLLER:
+ desc = (uintptr_t)controller_get_descriptor_by_name(name,
+ errp);
+ break;
+ case DM_MEDIA:
+ desc = (uintptr_t)media_get_descriptor_by_name(name, errp);
+ break;
+ case DM_SLICE:
+ desc = (uintptr_t)slice_get_descriptor_by_name(name, errp);
+ break;
+ case DM_PARTITION:
+ desc = (uintptr_t)partition_get_descriptor_by_name(name,
+ errp);
+ break;
+ case DM_PATH:
+ desc = (uintptr_t)path_get_descriptor_by_name(name, errp);
+ break;
+ case DM_ALIAS:
+ desc = (uintptr_t)alias_get_descriptor_by_name(name, errp);
+ break;
+ default:
+ *errp = EINVAL;
+ break;
+ }
+
+ cache_unlock();
+
+ return (desc);
+}
+
+dm_descriptor_t *
+dm_get_descriptors(dm_desc_type_t type, int filter[], int *errp)
+{
+ descriptor_t **descs = NULL;
+
+
+ cache_wlock();
+
+ switch (type) {
+ case DM_DRIVE:
+ descs = drive_get_descriptors(filter, errp);
+ break;
+ case DM_BUS:
+ descs = bus_get_descriptors(filter, errp);
+ break;
+ case DM_CONTROLLER:
+ descs = controller_get_descriptors(filter, errp);
+ break;
+ case DM_MEDIA:
+ descs = media_get_descriptors(filter, errp);
+ break;
+ case DM_SLICE:
+ descs = slice_get_descriptors(filter, errp);
+ break;
+ case DM_PARTITION:
+ descs = partition_get_descriptors(filter, errp);
+ break;
+ case DM_PATH:
+ descs = path_get_descriptors(filter, errp);
+ break;
+ case DM_ALIAS:
+ descs = alias_get_descriptors(filter, errp);
+ break;
+ default:
+ *errp = EINVAL;
+ break;
+ }
+
+ cache_unlock();
+
+ return (ptr_array_to_desc_array(descs, errp));
+}
+
+char *
+dm_get_name(dm_descriptor_t desc, int *errp)
+{
+ descriptor_t *dp;
+ char *nm = NULL;
+ char *name = NULL;
+
+ dp = (descriptor_t *)desc;
+
+ cache_rlock();
+
+ if (!cache_is_valid_desc(dp)) {
+ cache_unlock();
+ *errp = EBADF;
+ return (NULL);
+ }
+
+ /* verify that the descriptor is still valid */
+ if (dp->p.generic == NULL) {
+ cache_unlock();
+ *errp = ENODEV;
+ return (NULL);
+ }
+
+ switch (dp->type) {
+ case DM_DRIVE:
+ nm = (drive_get_name(dp));
+ break;
+ case DM_BUS:
+ nm = (bus_get_name(dp));
+ break;
+ case DM_CONTROLLER:
+ nm = (controller_get_name(dp));
+ break;
+ case DM_MEDIA:
+ nm = (media_get_name(dp));
+ break;
+ case DM_SLICE:
+ nm = (slice_get_name(dp));
+ break;
+ case DM_PARTITION:
+ nm = (partition_get_name(dp));
+ break;
+ case DM_PATH:
+ nm = (path_get_name(dp));
+ break;
+ case DM_ALIAS:
+ nm = (alias_get_name(dp));
+ break;
+ }
+
+ cache_unlock();
+
+ *errp = 0;
+ if (nm != NULL) {
+ name = strdup(nm);
+ if (name == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+ return (name);
+ }
+ return (NULL);
+}
+
+nvlist_t *
+dm_get_stats(dm_descriptor_t desc, int stat_type, int *errp)
+{
+ descriptor_t *dp;
+ nvlist_t *stats = NULL;
+
+
+ dp = (descriptor_t *)desc;
+
+ cache_rlock();
+
+ if (!cache_is_valid_desc(dp)) {
+ cache_unlock();
+ *errp = EBADF;
+ return (NULL);
+ }
+
+ /* verify that the descriptor is still valid */
+ if (dp->p.generic == NULL) {
+ cache_unlock();
+ *errp = ENODEV;
+ return (NULL);
+ }
+
+ switch (dp->type) {
+ case DM_DRIVE:
+ stats = drive_get_stats(dp, stat_type, errp);
+ break;
+ case DM_BUS:
+ stats = bus_get_stats(dp, stat_type, errp);
+ break;
+ case DM_CONTROLLER:
+ stats = controller_get_stats(dp, stat_type, errp);
+ break;
+ case DM_MEDIA:
+ stats = media_get_stats(dp, stat_type, errp);
+ break;
+ case DM_SLICE:
+ stats = slice_get_stats(dp, stat_type, errp);
+ break;
+ case DM_PARTITION:
+ stats = partition_get_stats(dp, stat_type, errp);
+ break;
+ case DM_PATH:
+ stats = path_get_stats(dp, stat_type, errp);
+ break;
+ case DM_ALIAS:
+ stats = alias_get_stats(dp, stat_type, errp);
+ break;
+ default:
+ *errp = EINVAL;
+ break;
+ }
+
+ cache_unlock();
+
+ return (stats);
+}
+
+dm_desc_type_t
+dm_get_type(dm_descriptor_t desc)
+{
+ descriptor_t *dp;
+
+ dp = (descriptor_t *)desc;
+
+ cache_rlock();
+
+ if (!cache_is_valid_desc(dp)) {
+ cache_unlock();
+ return (-1);
+ }
+
+ cache_unlock();
+
+ return (dp->type);
+}
+
+void
+libdiskmgt_add_str(nvlist_t *attrs, char *name, char *val, int *errp)
+{
+ if (*errp == 0) {
+ *errp = nvlist_add_string(attrs, name, val);
+ }
+}
+
+descriptor_t **
+libdiskmgt_empty_desc_array(int *errp)
+{
+ descriptor_t **empty;
+
+ empty = (descriptor_t **)calloc(1, sizeof (descriptor_t *));
+ if (empty == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+ empty[0] = NULL;
+
+ *errp = 0;
+ return (empty);
+}
+
+void
+libdiskmgt_init_debug()
+{
+ char *valp;
+
+ if ((valp = getenv(DM_DEBUG)) != NULL) {
+ dm_debug = atoi(valp);
+ }
+}
+
+int
+libdiskmgt_str_eq(char *nm1, char *nm2)
+{
+ if (nm1 == NULL) {
+ if (dm_debug) {
+ (void) fprintf(stderr, "WARNING: str_eq nm1 NULL\n");
+ }
+
+ if (nm2 == NULL) {
+ return (1);
+ } else {
+ return (0);
+ }
+ }
+
+ /* nm1 != NULL */
+
+ if (nm2 == NULL) {
+ if (dm_debug) {
+ (void) fprintf(stderr, "WARNING: str_eq nm2 NULL\n");
+ }
+ return (0);
+ }
+
+ if (strcmp(nm1, nm2) == 0) {
+ return (1);
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static descriptor_t **
+desc_array_to_ptr_array(dm_descriptor_t *descs, int *errp)
+{
+#ifdef _LP64
+ return ((descriptor_t **)descs);
+#else
+ /* convert the 64 bit descriptors to 32 bit ptrs */
+ int cnt;
+ int i;
+ descriptor_t **da;
+
+ for (cnt = 0; descs[cnt]; cnt++);
+
+ da = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
+ if (da == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ for (i = 0; descs[i]; i++) {
+ da[i] = (descriptor_t *)descs[i];
+ }
+ *errp = 0;
+ free(descs);
+
+ return (da);
+#endif
+}
+
+/*ARGSUSED*/
+static dm_descriptor_t *
+ptr_array_to_desc_array(descriptor_t **ptrs, int *errp)
+{
+#ifdef _LP64
+ return ((dm_descriptor_t *)ptrs);
+#else
+ /* convert the 32 bit ptrs to the 64 bit descriptors */
+ int cnt;
+ int i;
+ dm_descriptor_t *da;
+
+ if (*errp != 0 || ptrs == NULL) {
+ return (NULL);
+ }
+
+ for (cnt = 0; ptrs[cnt]; cnt++);
+
+ da = (dm_descriptor_t *)calloc(cnt + 1, sizeof (dm_descriptor_t));
+ if (da == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ for (i = 0; ptrs[i]; i++) {
+ da[i] = (uintptr_t)ptrs[i];
+ }
+ *errp = 0;
+ free(ptrs);
+
+ return (da);
+#endif
+}
diff --git a/usr/src/lib/libdiskmgt/common/events.c b/usr/src/lib/libdiskmgt/common/events.c
new file mode 100644
index 0000000000..e6dc869d2a
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/events.c
@@ -0,0 +1,496 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stropts.h>
+#include <synch.h>
+#include <thread.h>
+#include <libsysevent.h>
+#include <sys/sysevent/eventdefs.h>
+#include <sys/sysevent/dev.h>
+#include <errno.h>
+#include <libgen.h>
+#include <unistd.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+
+struct event_list {
+ struct event_list *next;
+ nvlist_t *event;
+};
+
+static struct event_list *events = NULL;
+static int event_error = 0;
+static int event_break = 0;
+static mutex_t queue_lock;
+static sema_t semaphore;
+
+/*
+ * When we add a controller we get an add event for each drive on the
+ * controller. We don't want to walk the devtree for each drive since
+ * we will get the same information each time. So, the solution is to
+ * wait for a few seconds for all of the add events to come in and then
+ * do a single walk. If an add event comes in after we start the walk, we
+ * need to do another walk since we might have missed that drive.
+ *
+ * State: 0 - no walker; 1 - walker waiting; 2 - walker running
+ * 0 -> 1; wait a few seconds
+ * 1 -> 2; walking the devtree
+ * 2 -> either 0 or 1 (see below)
+ * While running (state 2), if event comes in, go back to waiting (state 1)
+ * after the walk otherwise go back to none (state 0).
+ *
+ * walker_lock protects walker_state & events_pending
+ */
+#define WALK_NONE 0
+#define WALK_WAITING 1
+#define WALK_RUNNING 2
+#define WALK_WAIT_TIME 60 /* wait 60 seconds */
+
+static mutex_t walker_lock;
+static int walker_state = WALK_NONE;
+static int events_pending = 0;
+
+static int sendevents = 0;
+
+static void add_event_to_queue(nvlist_t *event);
+static void cb_watch_events();
+static void event_handler(sysevent_t *ev);
+static void print_nvlist(char *prefix, nvlist_t *list);
+static void walk_devtree();
+static void walker(void *arg);
+
+static void(*callback)(nvlist_t *, int) = NULL;
+
+nvlist_t *
+dm_get_event(int *errp)
+{
+ nvlist_t *event = NULL;
+
+ *errp = 0;
+
+ /* wait until there is an event in the queue */
+ /*CONSTCOND*/
+ while (1) {
+ (void) sema_wait(&semaphore);
+
+ if (event_break) {
+ event_break = 0;
+ *errp = EINTR;
+ break;
+ }
+
+ (void) mutex_lock(&queue_lock);
+
+ /* first see if we ran out of memory since the last call */
+ if (event_error != 0) {
+ *errp = event_error;
+ event_error = 0;
+
+ } else if (events != NULL) {
+ struct event_list *tmpp;
+
+ event = events->event;
+ tmpp = events->next;
+ free(events);
+ events = tmpp;
+ }
+
+ (void) mutex_unlock(&queue_lock);
+
+ if (*errp != 0 || event != NULL) {
+ break;
+ }
+ }
+
+ return (event);
+}
+
+void
+dm_init_event_queue(void (*cb)(nvlist_t *, int), int *errp)
+{
+ if (sendevents == 1) {
+ /* we were already initialized, see what changes to make */
+ *errp = 0;
+ if (cb != callback) {
+
+ callback = cb;
+ if (cb == NULL) {
+ /* clearing the cb so shutdown the internal cb thread */
+ event_break = 1;
+ (void) sema_post(&semaphore);
+
+ } else {
+ /* installing a cb; we didn't have one before */
+ thread_t watch_thread;
+
+ *errp = thr_create(NULL, NULL,
+ (void *(*)(void *))cb_watch_events, NULL, THR_DAEMON,
+ &watch_thread);
+ }
+ }
+
+ } else {
+ /* first time to initialize */
+ sendevents = 1;
+
+ *errp = sema_init(&semaphore, 0, USYNC_THREAD, NULL);
+ if (*errp != 0) {
+ return;
+ }
+
+ if (cb != NULL) {
+ thread_t watch_thread;
+
+ callback = cb;
+
+ *errp = thr_create(NULL, NULL,
+ (void *(*)(void *))cb_watch_events, NULL, THR_DAEMON,
+ &watch_thread);
+ }
+ }
+}
+
+void
+events_new_event(char *name, int dtype, char *etype)
+{
+ nvlist_t *event = NULL;
+
+ if (!sendevents) {
+ return;
+ }
+
+ if (nvlist_alloc(&event, NVATTRS, 0) != 0) {
+ event = NULL;
+
+ } else {
+ int error = 0;
+
+ if (name != NULL &&
+ nvlist_add_string(event, DM_EV_NAME, name) != 0) {
+ error = ENOMEM;
+ }
+
+ if (dtype != -1 &&
+ nvlist_add_uint32(event, DM_EV_DTYPE, dtype) != 0) {
+ error = ENOMEM;
+ }
+
+ if (nvlist_add_string(event, DM_EV_TYPE, etype) != 0) {
+ error = ENOMEM;
+ }
+
+ if (error != 0) {
+ nvlist_free(event);
+ event = NULL;
+ }
+ }
+
+ add_event_to_queue(event);
+}
+
+void
+events_new_slice_event(char *dev, char *type)
+{
+ events_new_event(basename(dev), DM_SLICE, type);
+}
+
+int
+events_start_event_watcher()
+{
+ sysevent_handle_t *shp;
+ const char *subclass_list[1];
+
+ /* Bind event handler and create subscriber handle */
+ shp = sysevent_bind_handle(event_handler);
+ if (shp == NULL) {
+ if (dm_debug) {
+ /* keep going when we're debugging */
+ (void) fprintf(stderr, "ERROR: bind failed %d\n", errno);
+ return (0);
+ }
+ return (errno);
+ }
+
+ subclass_list[0] = ESC_DISK;
+ if (sysevent_subscribe_event(shp, EC_DEV_ADD, subclass_list, 1) != 0) {
+ if (dm_debug) {
+ /* keep going when we're debugging */
+ (void) fprintf(stderr, "ERROR: subscribe failed\n");
+ return (0);
+ }
+ return (errno);
+ }
+ if (sysevent_subscribe_event(shp, EC_DEV_REMOVE, subclass_list, 1)
+ != 0) {
+ if (dm_debug) {
+ /* keep going when we're debugging */
+ (void) fprintf(stderr, "ERROR: subscribe failed\n");
+ return (0);
+ }
+ return (errno);
+ }
+
+ return (0);
+}
+
+static void
+add_event_to_queue(nvlist_t *event)
+{
+ (void) mutex_lock(&queue_lock);
+
+ if (event == NULL) {
+ event_error = ENOMEM;
+ (void) mutex_unlock(&queue_lock);
+ return;
+ }
+
+ if (events == NULL) {
+
+ events = (struct event_list *)malloc(sizeof (struct event_list));
+ if (events == NULL) {
+ event_error = ENOMEM;
+ nvlist_free(event);
+ } else {
+ events->next = NULL;
+ events->event = event;
+ }
+
+ } else {
+ /* already have events in the queue */
+ struct event_list *ep;
+ struct event_list *new_event;
+
+ /* find the last element in the list */
+ for (ep = events; ep->next != NULL; ep = ep->next);
+
+ new_event = (struct event_list *)malloc(sizeof (struct event_list));
+ if (new_event == NULL) {
+ event_error = ENOMEM;
+ nvlist_free(event);
+ } else {
+ new_event->next = NULL;
+ new_event->event = event;
+ ep->next = new_event;
+ }
+ }
+
+ (void) mutex_unlock(&queue_lock);
+
+ (void) sema_post(&semaphore);
+}
+
+static void
+cb_watch_events()
+{
+ nvlist_t *event;
+ int error;
+
+ /*CONSTCOND*/
+ while (1) {
+ event = dm_get_event(&error);
+ if (callback == NULL) {
+ /* end the thread */
+ return;
+ }
+ callback(event, error);
+ }
+}
+
+static void
+event_handler(sysevent_t *ev)
+{
+ char *class_name;
+ char *pub;
+
+ class_name = sysevent_get_class_name(ev);
+ if (dm_debug) {
+ (void) fprintf(stderr, "****EVENT: %s %s ", class_name,
+ sysevent_get_subclass_name(ev));
+ if ((pub = sysevent_get_pub_name(ev)) != NULL) {
+ (void) fprintf(stderr, "%s\n", pub);
+ free(pub);
+ } else {
+ (void) fprintf(stderr, "\n");
+ }
+ }
+
+ if (libdiskmgt_str_eq(class_name, EC_DEV_ADD)) {
+ /* batch up the adds into a single devtree walk */
+ walk_devtree();
+
+ } else if (libdiskmgt_str_eq(class_name, EC_DEV_REMOVE)) {
+ nvlist_t *nvlist = NULL;
+ char *dev_name = NULL;
+
+ (void) sysevent_get_attr_list(ev, &nvlist);
+ if (nvlist != NULL) {
+ (void) nvlist_lookup_string(nvlist, DEV_NAME, &dev_name);
+
+ if (dm_debug) {
+ print_nvlist("**** ", nvlist);
+ }
+ }
+
+ if (dev_name != NULL) {
+ cache_update(DM_EV_DISK_DELETE, dev_name);
+ }
+
+ if (nvlist != NULL) {
+ nvlist_free(nvlist);
+ }
+ }
+}
+
+/*
+ * This is a debugging function only.
+ */
+static void
+print_nvlist(char *prefix, nvlist_t *list)
+{
+ nvpair_t *nvp;
+
+ nvp = nvlist_next_nvpair(list, NULL);
+ while (nvp != NULL) {
+ char *attrname;
+ char *str;
+ uint32_t ui32;
+ uint64_t ui64;
+ char **str_array;
+ uint_t cnt;
+ int i;
+
+ attrname = nvpair_name(nvp);
+ switch (nvpair_type(nvp)) {
+ case DATA_TYPE_STRING:
+ (void) nvpair_value_string(nvp, &str);
+ (void) fprintf(stderr, "%s%s: %s\n", prefix, attrname, str);
+ break;
+
+ case DATA_TYPE_STRING_ARRAY:
+ (void) nvpair_value_string_array(nvp, &str_array, &cnt);
+ (void) fprintf(stderr, "%s%s:\n", prefix, attrname);
+ for (i = 0; i < cnt; i++) {
+ (void) fprintf(stderr, "%s %s\n", prefix, str_array[i]);
+ }
+ break;
+
+ case DATA_TYPE_UINT32:
+ (void) nvpair_value_uint32(nvp, &ui32);
+ (void) fprintf(stderr, "%s%s: %u\n", prefix, attrname, ui32);
+ break;
+
+ case DATA_TYPE_UINT64:
+ (void) nvpair_value_uint64(nvp, &ui64);
+#ifdef _LP64
+ (void) fprintf(stderr, "%s%s: %lu\n", prefix, attrname, ui64);
+#else
+ (void) fprintf(stderr, "%s%s: %llu\n", prefix, attrname, ui64);
+#endif
+ break;
+
+
+ case DATA_TYPE_BOOLEAN:
+ (void) fprintf(stderr, "%s%s: true\n", prefix, attrname);
+ break;
+
+ default:
+ (void) fprintf(stderr, "%s%s: UNSUPPORTED TYPE\n", prefix,
+ attrname);
+ break;
+ }
+
+ nvp = nvlist_next_nvpair(list, nvp);
+ }
+}
+
+/*
+ * Batch up the adds into a single devtree walk. We can get a bunch of
+ * adds when we add a controller since we will get an add event for each
+ * drive.
+ */
+static void
+walk_devtree()
+{
+ thread_t walk_thread;
+
+ (void) mutex_lock(&walker_lock);
+
+ switch (walker_state) {
+ case WALK_NONE:
+ if (thr_create(NULL, NULL, (void *(*)(void *))walker, NULL,
+ THR_DAEMON, &walk_thread) == 0) {
+ walker_state = WALK_WAITING;
+ }
+ break;
+
+ case WALK_WAITING:
+ /* absorb the event and do nothing */
+ break;
+
+ case WALK_RUNNING:
+ events_pending = 1;
+ break;
+ }
+
+ (void) mutex_unlock(&walker_lock);
+}
+
+/*ARGSUSED*/
+static void
+walker(void *arg)
+{
+ int walk_again = 0;
+
+ do {
+ /* start by wating for a few seconds to absorb extra events */
+ (void) sleep(WALK_WAIT_TIME);
+
+ (void) mutex_lock(&walker_lock);
+ walker_state = WALK_RUNNING;
+ (void) mutex_unlock(&walker_lock);
+
+ cache_update(DM_EV_DISK_ADD, NULL);
+
+ (void) mutex_lock(&walker_lock);
+
+ if (events_pending) {
+ events_pending = 0;
+ walker_state = WALK_WAITING;
+ walk_again = 1;
+ } else {
+ walker_state = WALK_NONE;
+ walk_again = 0;
+ }
+
+ (void) mutex_unlock(&walker_lock);
+
+ } while (walk_again);
+}
diff --git a/usr/src/lib/libdiskmgt/common/findevs.c b/usr/src/lib/libdiskmgt/common/findevs.c
new file mode 100644
index 0000000000..39a9ddfd16
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/findevs.c
@@ -0,0 +1,1885 @@
+/*
+ * 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 <fcntl.h>
+#include <libdevinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/sunddi.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <devid.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+
+#define CLUSTER_DEV "did"
+
+/* specify which disk links to use in the /dev directory */
+#define DEVLINK_REGEX "rdsk/.*"
+#define DEVLINK_FLOPPY_REGEX "rdiskette[0-9]"
+#define DEVLINK_DID_REGEX "did/rdsk/.*"
+
+#define FLOPPY_NAME "rdiskette"
+
+#define MAXPROPLEN 1024
+#define DEVICE_ID_PROP "devid"
+#define PROD_ID_PROP "inquiry-product-id"
+#define PROD_ID_USB_PROP "usb-product-name"
+#define REMOVABLE_PROP "removable-media"
+#define SCSI_OPTIONS_PROP "scsi-options"
+#define VENDOR_ID_PROP "inquiry-vendor-id"
+#define VENDOR_ID_USB_PROP "usb-vendor-name"
+#define WWN_PROP "node-wwn"
+
+/* The list of names of possible disk types used by libdevinfo. */
+static char *disktypes[] = {
+ DDI_NT_BLOCK,
+ DDI_NT_BLOCK_CHAN,
+ DDI_NT_BLOCK_WWN,
+ DDI_NT_BLOCK_FABRIC,
+ DDI_NT_CD_CHAN,
+ DDI_NT_CD,
+ DDI_NT_FD,
+ NULL
+};
+
+/*
+ * Most of the removable media will be lumped under here; CD, DVD, MO, etc.
+ */
+static char *cdromtypes[] = {
+ DDI_NT_CD_CHAN,
+ DDI_NT_CD,
+ NULL
+};
+
+static char *ctrltypes[] = {
+ DDI_NT_SCSI_NEXUS,
+ DDI_NT_SCSI_ATTACHMENT_POINT,
+ DDI_NT_FC_ATTACHMENT_POINT,
+ NULL
+};
+
+static char *bustypes[] = {
+ "sbus",
+ "pci",
+ "usb",
+ NULL
+};
+
+static bus_t *add_bus(struct search_args *args, di_node_t node,
+ di_minor_t minor, controller_t *cp);
+static int add_cluster_devs(di_node_t node, di_minor_t minor,
+ void *arg);
+static controller_t *add_controller(struct search_args *args,
+ di_node_t node, di_minor_t minor);
+static int add_devpath(di_devlink_t devlink, void *arg);
+static int add_devs(di_node_t node, di_minor_t minor, void *arg);
+static int add_disk2controller(disk_t *diskp,
+ struct search_args *args);
+static int add_disk2path(disk_t *dp, path_t *pp,
+ di_path_state_t st, char *wwn);
+static int add_int2array(int p, int **parray);
+static int add_ptr2array(void *p, void ***parray);
+static char *bus_type(di_node_t node, di_minor_t minor,
+ di_prom_handle_t ph);
+static int can_remove_controller(controller_t *cp,
+ controller_t *currp);
+static void clean_paths(struct search_args *args);
+static disk_t *create_disk(char *deviceid, char *kernel_name,
+ struct search_args *args);
+static char *ctype(di_node_t node, di_minor_t minor);
+static boolean_t disk_is_cdrom(char *type);
+static alias_t *find_alias(disk_t *diskp, char *kernel_name);
+static bus_t *find_bus(struct search_args *args, char *name);
+static controller_t *find_controller(struct search_args *args, char *name);
+static int fix_cluster_devpath(di_devlink_t devlink, void *arg);
+static disk_t *get_disk_by_deviceid(disk_t *listp, char *devid);
+static void get_disk_name_from_path(char *path, char *name,
+ int size);
+static char *get_byte_prop(char *prop_name, di_node_t node);
+static di_node_t get_parent_bus(di_node_t node,
+ struct search_args *args);
+static int get_prom_int(char *prop_name, di_node_t node,
+ di_prom_handle_t ph);
+static char *get_prom_str(char *prop_name, di_node_t node,
+ di_prom_handle_t ph);
+static int get_prop(char *prop_name, di_node_t node);
+static char *get_str_prop(char *prop_name, di_node_t node);
+static int have_disk(struct search_args *args, char *devid,
+ char *kernel_name, disk_t **diskp);
+static int is_cluster_disk(di_node_t node, di_minor_t minor);
+static int is_ctds(char *name);
+static int is_drive(di_minor_t minor);
+static int is_HBA(di_node_t node, di_minor_t minor);
+static int new_alias(disk_t *diskp, char *kernel_path,
+ char *devlink_path, struct search_args *args);
+static int new_devpath(alias_t *ap, char *devpath);
+static path_t *new_path(controller_t *cp, disk_t *diskp,
+ di_node_t node, di_path_state_t st, char *wwn);
+static void remove_invalid_controller(char *name,
+ controller_t *currp, struct search_args *args);
+static char *str_case_index(register char *s1, register char *s2);
+
+/*
+ * The functions in this file do a dev tree walk to build up a model of the
+ * disks, controllers and paths on the system. This model is returned in the
+ * args->disk_listp and args->controller_listp members of the args param.
+ * There is no global data for this file so it is thread safe. It is up to
+ * the caller to merge the resulting model with any existing model that is
+ * cached. The caller must also free the memory for this model when it is
+ * no longer needed.
+ */
+void
+findevs(struct search_args *args)
+{
+ uint_t flags;
+ di_node_t di_root;
+
+ args->dev_walk_status = 0;
+ args->disk_listp = NULL;
+ args->controller_listp = NULL;
+ args->bus_listp = NULL;
+
+ args->handle = di_devlink_init(NULL, 0);
+
+ /*
+ * Have to make several passes at this with the new devfs caching.
+ * First, we find non-mpxio devices. Then we find mpxio/multipath
+ * devices. Finally, we get cluster devices.
+ */
+ flags = DINFOCACHE;
+ di_root = di_init("/", flags);
+ args->ph = di_prom_init();
+ (void) di_walk_minor(di_root, NULL, 0, args, add_devs);
+ di_fini(di_root);
+
+ flags = DINFOCPYALL | DINFOPATH;
+ di_root = di_init("/", flags);
+ (void) di_walk_minor(di_root, NULL, 0, args, add_devs);
+ di_fini(di_root);
+
+ /* do another pass to clean up cluster devpaths */
+ flags = DINFOCACHE;
+ di_root = di_init("/", flags);
+ (void) di_walk_minor(di_root, DDI_PSEUDO, 0, args, add_cluster_devs);
+ if (args->ph != DI_PROM_HANDLE_NIL) {
+ (void) di_prom_fini(args->ph);
+ }
+ di_fini(di_root);
+
+ (void) di_devlink_fini(&(args->handle));
+
+ clean_paths(args);
+}
+
+/*
+ * Definitions of private functions
+ */
+
+static bus_t *
+add_bus(struct search_args *args, di_node_t node, di_minor_t minor,
+ controller_t *cp)
+{
+ char *btype;
+ char *devpath;
+ bus_t *bp;
+ char kstat_name[MAXPATHLEN];
+ di_node_t pnode;
+
+ if (node == DI_NODE_NIL) {
+ return (NULL);
+ }
+
+ if ((btype = bus_type(node, minor, args->ph)) == NULL) {
+ return (add_bus(args, di_parent_node(node),
+ di_minor_next(di_parent_node(node), NULL), cp));
+ }
+
+ devpath = di_devfs_path(node);
+
+ if ((bp = find_bus(args, devpath)) != NULL) {
+ di_devfs_path_free((void *) devpath);
+
+ if (cp != NULL) {
+ if (add_ptr2array(cp, (void ***)&bp->controllers) != 0) {
+ args->dev_walk_status = ENOMEM;
+ return (NULL);
+ }
+ }
+ return (bp);
+ }
+
+ /* Special handling for root node. */
+ if (strcmp(devpath, "/") == 0) {
+ di_devfs_path_free((void *) devpath);
+ return (NULL);
+ }
+
+ if (dm_debug) {
+ (void) fprintf(stderr, "INFO: add_bus %s\n", devpath);
+ }
+
+ bp = (bus_t *)calloc(1, sizeof (bus_t));
+ if (bp == NULL) {
+ return (NULL);
+ }
+
+ bp->name = strdup(devpath);
+ di_devfs_path_free((void *) devpath);
+ if (bp->name == NULL) {
+ args->dev_walk_status = ENOMEM;
+ cache_free_bus(bp);
+ return (NULL);
+ }
+
+ bp->btype = strdup(btype);
+ if (bp->btype == NULL) {
+ args->dev_walk_status = ENOMEM;
+ cache_free_bus(bp);
+ return (NULL);
+ }
+
+ (void) snprintf(kstat_name, sizeof (kstat_name), "%s%d",
+ di_node_name(node), di_instance(node));
+
+ if ((bp->kstat_name = strdup(kstat_name)) == NULL) {
+ args->dev_walk_status = ENOMEM;
+ cache_free_bus(bp);
+ return (NULL);
+ }
+
+ /* if parent node is a bus, get its name */
+ if ((pnode = get_parent_bus(node, args)) != NULL) {
+ devpath = di_devfs_path(pnode);
+ bp->pname = strdup(devpath);
+ di_devfs_path_free((void *) devpath);
+ if (bp->pname == NULL) {
+ args->dev_walk_status = ENOMEM;
+ cache_free_bus(bp);
+ return (NULL);
+ }
+
+ } else {
+ bp->pname = NULL;
+ }
+
+ bp->freq = get_prom_int("clock-frequency", node, args->ph);
+
+ bp->controllers = (controller_t **)calloc(1, sizeof (controller_t *));
+ if (bp->controllers == NULL) {
+ args->dev_walk_status = ENOMEM;
+ cache_free_bus(bp);
+ return (NULL);
+ }
+ bp->controllers[0] = NULL;
+
+ if (cp != NULL) {
+ if (add_ptr2array(cp, (void ***)&bp->controllers) != 0) {
+ args->dev_walk_status = ENOMEM;
+ return (NULL);
+ }
+ }
+
+ bp->next = args->bus_listp;
+ args->bus_listp = bp;
+
+ return (bp);
+}
+
+static int
+add_cluster_devs(di_node_t node, di_minor_t minor, void *arg)
+{
+ struct search_args *args;
+ char *devpath;
+ char slice_path[MAXPATHLEN];
+ int result = DI_WALK_CONTINUE;
+
+ if (!is_cluster_disk(node, minor)) {
+ return (DI_WALK_CONTINUE);
+ }
+
+ args = (struct search_args *)arg;
+
+ if (dm_debug > 1) {
+ /* This is all just debugging code */
+ char *devpath;
+ char dev_name[MAXPATHLEN];
+
+ devpath = di_devfs_path(node);
+ (void) snprintf(dev_name, sizeof (dev_name), "%s:%s", devpath,
+ di_minor_name(minor));
+ di_devfs_path_free((void *) devpath);
+
+ (void) fprintf(stderr, "INFO: cluster dev: %s\n", dev_name);
+ }
+
+ args->node = node;
+ args->minor = minor;
+ args->dev_walk_status = 0;
+
+ /*
+ * Fix the devpaths for the cluster drive.
+ *
+ * We will come through here once for each raw slice device name.
+ */
+ devpath = di_devfs_path(node);
+ (void) snprintf(slice_path, sizeof (slice_path), "%s:%s", devpath,
+ di_minor_name(minor));
+ di_devfs_path_free((void *) devpath);
+
+ /* Walk the /dev tree to get the cluster devlinks. */
+ (void) di_devlink_walk(args->handle, DEVLINK_DID_REGEX, slice_path,
+ DI_PRIMARY_LINK, arg, fix_cluster_devpath);
+
+ if (args->dev_walk_status != 0) {
+ result = DI_WALK_TERMINATE;
+ }
+
+ return (result);
+}
+
+static controller_t *
+add_controller(struct search_args *args, di_node_t node, di_minor_t minor)
+{
+ char *devpath;
+ controller_t *cp;
+ char kstat_name[MAXPATHLEN];
+ char *c_type = DM_CTYPE_UNKNOWN;
+
+ devpath = di_devfs_path(node);
+
+ if ((cp = find_controller(args, devpath)) != NULL) {
+ di_devfs_path_free((void *) devpath);
+ return (cp);
+ }
+
+ /* Special handling for fp attachment node. */
+ if (strcmp(di_node_name(node), "fp") == 0) {
+ di_node_t pnode;
+
+ pnode = di_parent_node(node);
+ if (pnode != DI_NODE_NIL) {
+ di_devfs_path_free((void *) devpath);
+ devpath = di_devfs_path(pnode);
+
+ if ((cp = find_controller(args, devpath)) != NULL) {
+ di_devfs_path_free((void *) devpath);
+ return (cp);
+ }
+
+ /* not in the list, create it */
+ node = pnode;
+ c_type = DM_CTYPE_FIBRE;
+ }
+ }
+
+ if (dm_debug) {
+ (void) fprintf(stderr, "INFO: add_controller %s\n", devpath);
+ }
+
+ cp = (controller_t *)calloc(1, sizeof (controller_t));
+ if (cp == NULL) {
+ return (NULL);
+ }
+
+ cp->name = strdup(devpath);
+ di_devfs_path_free((void *) devpath);
+ if (cp->name == NULL) {
+ cache_free_controller(cp);
+ return (NULL);
+ }
+
+ if (strcmp(c_type, DM_CTYPE_UNKNOWN) == 0) {
+ c_type = ctype(node, minor);
+ }
+ cp->ctype = c_type;
+
+ (void) snprintf(kstat_name, sizeof (kstat_name), "%s%d",
+ di_node_name(node), di_instance(node));
+
+ if ((cp->kstat_name = strdup(kstat_name)) == NULL) {
+ cache_free_controller(cp);
+ return (NULL);
+ }
+
+ if (libdiskmgt_str_eq(cp->ctype, "scsi")) {
+ cp->scsi_options = get_prop(SCSI_OPTIONS_PROP, node);
+ }
+
+ if (libdiskmgt_str_eq(di_node_name(node), "scsi_vhci")) {
+ cp->multiplex = 1;
+ } else {
+ cp->multiplex = 0;
+ }
+
+ cp->freq = get_prom_int("clock-frequency", node, args->ph);
+
+ cp->disks = (disk_t **)calloc(1, sizeof (disk_t *));
+ if (cp->disks == NULL) {
+ cache_free_controller(cp);
+ return (NULL);
+ }
+ cp->disks[0] = NULL;
+
+ cp->next = args->controller_listp;
+ args->controller_listp = cp;
+
+ cp->bus = add_bus(args, di_parent_node(node),
+ di_minor_next(di_parent_node(node), NULL), cp);
+
+ return (cp);
+}
+
+static int
+add_devpath(di_devlink_t devlink, void *arg)
+{
+ struct search_args *args;
+ char *devidstr;
+ disk_t *diskp;
+ char kernel_name[MAXPATHLEN];
+
+ args = (struct search_args *)arg;
+
+ /*
+ * Get the diskp value from calling have_disk. Can either be found
+ * by kernel name or devid.
+ */
+
+ diskp = NULL;
+ devidstr = get_str_prop(DEVICE_ID_PROP, args->node);
+ (void) snprintf(kernel_name, sizeof (kernel_name), "%s%d",
+ di_node_name(args->node), di_instance(args->node));
+
+ (void) have_disk(args, devidstr, kernel_name, &diskp);
+
+ /*
+ * The devlink_path is usually of the form /dev/rdsk/c0t0d0s0.
+ * For diskettes it is /dev/rdiskette*.
+ * On Intel we would also get each fdisk partition as well
+ * (e.g. /dev/rdsk/c0t0d0p0).
+ */
+ if (diskp != NULL) {
+ alias_t *ap;
+ char *devlink_path;
+
+ if (diskp->drv_type != DM_DT_FLOPPY) {
+ /*
+ * Add other controllers for multipath disks. This will have
+ * no effect if the controller relationship is already set up.
+ */
+ if (add_disk2controller(diskp, args) != 0) {
+ args->dev_walk_status = ENOMEM;
+ }
+ }
+
+ (void) snprintf(kernel_name, sizeof (kernel_name), "%s%d",
+ di_node_name(args->node), di_instance(args->node));
+ devlink_path = (char *)di_devlink_path(devlink);
+
+ if (dm_debug > 1) {
+ (void) fprintf(stderr, "INFO: devpath %s\n", devlink_path);
+ }
+
+ if ((ap = find_alias(diskp, kernel_name)) == NULL) {
+ if (new_alias(diskp, kernel_name, devlink_path, args) != 0) {
+ args->dev_walk_status = ENOMEM;
+ }
+ } else {
+ /*
+ * It is possible that we have already added this devpath.
+ * Do not add it again. new_devpath will return a 0 if
+ * found, and not add the path.
+ */
+ if (new_devpath(ap, devlink_path) != 0) {
+ args->dev_walk_status = ENOMEM;
+ }
+ }
+ }
+
+ return (DI_WALK_CONTINUE);
+}
+
+static int
+add_devs(di_node_t node, di_minor_t minor, void *arg)
+{
+ struct search_args *args;
+ int result = DI_WALK_CONTINUE;
+
+ args = (struct search_args *)arg;
+
+ if (dm_debug > 1) {
+ /* This is all just debugging code */
+ char *devpath;
+ char dev_name[MAXPATHLEN];
+
+ devpath = di_devfs_path(node);
+ (void) snprintf(dev_name, sizeof (dev_name), "%s:%s", devpath,
+ di_minor_name(minor));
+ di_devfs_path_free((void *) devpath);
+
+ (void) fprintf(stderr,
+ "INFO: dev: %s, node: %s%d, minor: 0x%x, type: %s\n",
+ dev_name,
+ di_node_name(node), di_instance(node),
+ di_minor_spectype(minor),
+ (di_minor_nodetype(minor) != NULL ?
+ di_minor_nodetype(minor) : "NULL"));
+ }
+
+ if (bus_type(node, minor, args->ph) != NULL) {
+ if (add_bus(args, node, minor, NULL) == NULL) {
+ args->dev_walk_status = ENOMEM;
+ result = DI_WALK_TERMINATE;
+ }
+
+ } else if (is_HBA(node, minor)) {
+ if (add_controller(args, node, minor) == NULL) {
+ args->dev_walk_status = ENOMEM;
+ result = DI_WALK_TERMINATE;
+ }
+
+ } else if (di_minor_spectype(minor) == S_IFCHR && is_drive(minor)) {
+ char *devidstr;
+ char kernel_name[MAXPATHLEN];
+ disk_t *diskp;
+
+ (void) snprintf(kernel_name, sizeof (kernel_name), "%s%d",
+ di_node_name(node), di_instance(node));
+ devidstr = get_str_prop(DEVICE_ID_PROP, node);
+
+ args->node = node;
+ args->minor = minor;
+
+ /* Check if we already got this disk and this is another slice */
+ if (!have_disk(args, devidstr, kernel_name, &diskp)) {
+
+ args->dev_walk_status = 0;
+ /* This is a newly found disk, create the disk structure. */
+ diskp = create_disk(devidstr, kernel_name, args);
+ if (diskp == NULL) {
+ args->dev_walk_status = ENOMEM;
+ }
+
+ if (diskp->drv_type != DM_DT_FLOPPY) {
+ /* add the controller relationship */
+ if (args->dev_walk_status == 0) {
+ if (add_disk2controller(diskp, args) != 0) {
+ args->dev_walk_status = ENOMEM;
+ }
+ }
+ }
+ }
+
+ /* Add the devpaths for the drive. */
+ if (args->dev_walk_status == 0) {
+ char *devpath;
+ char slice_path[MAXPATHLEN];
+ char *pattern;
+
+ /*
+ * We will come through here once for each of the raw slice
+ * device names.
+ */
+ devpath = di_devfs_path(node);
+ (void) snprintf(slice_path, sizeof (slice_path), "%s:%s",
+ devpath, di_minor_name(minor));
+ di_devfs_path_free((void *) devpath);
+
+ if (libdiskmgt_str_eq(di_minor_nodetype(minor), DDI_NT_FD)) {
+ pattern = DEVLINK_FLOPPY_REGEX;
+ } else {
+ pattern = DEVLINK_REGEX;
+ }
+
+ /* Walk the /dev tree to get the devlinks. */
+ (void) di_devlink_walk(args->handle, pattern, slice_path,
+ DI_PRIMARY_LINK, arg, add_devpath);
+ }
+
+ if (args->dev_walk_status != 0) {
+ result = DI_WALK_TERMINATE;
+ }
+ }
+
+ return (result);
+}
+
+static int
+add_disk2controller(disk_t *diskp, struct search_args *args)
+{
+ di_node_t pnode;
+ controller_t *cp;
+ di_minor_t minor;
+ di_node_t node;
+ int i;
+
+ node = args->node;
+
+ pnode = di_parent_node(node);
+ if (pnode == DI_NODE_NIL) {
+ return (0);
+ }
+
+ minor = di_minor_next(pnode, NULL);
+ if (minor == NULL) {
+ return (0);
+ }
+
+ if ((cp = add_controller(args, pnode, minor)) == NULL) {
+ return (ENOMEM);
+ }
+
+ /* check if the disk <-> ctrl assoc is already there */
+ for (i = 0; diskp->controllers[i]; i++) {
+ if (cp == diskp->controllers[i]) {
+ return (0);
+ }
+ }
+
+ /* this is a new controller for this disk */
+
+ /* add the disk to the controlller */
+ if (add_ptr2array(diskp, (void ***)&cp->disks) != 0) {
+ return (ENOMEM);
+ }
+
+ /* add the controlller to the disk */
+ if (add_ptr2array(cp, (void ***)&diskp->controllers) != 0) {
+ return (ENOMEM);
+ }
+
+ /*
+ * Set up paths for mpxio controlled drives.
+ */
+ if (libdiskmgt_str_eq(di_node_name(pnode), "scsi_vhci")) {
+ /* note: mpxio di_path stuff is all consolidation private */
+ di_path_t pi = DI_PATH_NIL;
+
+ while ((pi = di_path_next_phci(node, pi)) != DI_PATH_NIL) {
+ int cnt;
+ uchar_t *bytes;
+ char str[MAXPATHLEN];
+ char *wwn;
+
+ di_node_t phci_node = di_path_phci_node(pi);
+
+ /* get the node wwn */
+ cnt = di_path_prop_lookup_bytes(pi, WWN_PROP, &bytes);
+ wwn = NULL;
+ if (cnt > 0) {
+ int i;
+
+ str[0] = 0;
+ for (i = 0; i < cnt; i++) {
+ char bstr[8]; /* a byte is only 2 hex chars + null */
+
+ (void) snprintf(bstr, sizeof (bstr), "%.2x", bytes[i]);
+ (void) strlcat(str, bstr, sizeof (str));
+ }
+ wwn = str;
+ }
+
+ if (new_path(cp, diskp, phci_node, di_path_state(pi), wwn)
+ == NULL) {
+ return (ENOMEM);
+ }
+ }
+ }
+
+ return (0);
+}
+
+static int
+add_disk2path(disk_t *dp, path_t *pp, di_path_state_t st, char *wwn)
+{
+ /* add the disk to the path */
+ if (add_ptr2array(dp, (void ***)&pp->disks) != 0) {
+ cache_free_path(pp);
+ return (0);
+ }
+
+ /* add the path to the disk */
+ if (add_ptr2array(pp, (void ***)&dp->paths) != 0) {
+ cache_free_path(pp);
+ return (0);
+ }
+
+ /* add the path state for this disk */
+ if (add_int2array(st, &pp->states) != 0) {
+ cache_free_path(pp);
+ return (0);
+ }
+
+ /* add the path state for this disk */
+ if (wwn != NULL) {
+ char *wp;
+
+ if ((wp = strdup(wwn)) != NULL) {
+ if (add_ptr2array(wp, (void ***)(&pp->wwns)) != 0) {
+ cache_free_path(pp);
+ return (0);
+ }
+ }
+ }
+
+ return (1);
+}
+
+static int
+add_int2array(int p, int **parray)
+{
+ int i;
+ int cnt;
+ int *pa;
+ int *new_array;
+
+ pa = *parray;
+
+ cnt = 0;
+ if (pa != NULL) {
+ for (; pa[cnt] != -1; cnt++);
+ }
+
+ new_array = (int *)calloc(cnt + 2, sizeof (int *));
+ if (new_array == NULL) {
+ return (ENOMEM);
+ }
+
+ /* copy the existing array */
+ for (i = 0; i < cnt; i++) {
+ new_array[i] = pa[i];
+ }
+
+ new_array[i] = p;
+ new_array[i + 1] = -1;
+
+ free(pa);
+ *parray = new_array;
+
+ return (0);
+}
+
+static int
+add_ptr2array(void *p, void ***parray)
+{
+ int i;
+ int cnt;
+ void **pa;
+ void **new_array;
+
+ pa = *parray;
+
+ cnt = 0;
+ if (pa != NULL) {
+ for (; pa[cnt]; cnt++);
+ }
+
+ new_array = (void **)calloc(cnt + 2, sizeof (void *));
+ if (new_array == NULL) {
+ return (ENOMEM);
+ }
+
+ /* copy the existing array */
+ for (i = 0; i < cnt; i++) {
+ new_array[i] = pa[i];
+ }
+
+ new_array[i] = p;
+ new_array[i + 1] = NULL;
+
+ free(pa);
+ *parray = new_array;
+
+ return (0);
+}
+
+/*
+ * This double checks that we aren't going to get into a bad situation.
+ * This function should never fail, but I just want to double check things.
+ */
+static int
+can_remove_controller(controller_t *cp, controller_t *currp)
+{
+ if (dm_debug) {
+ if (cp == currp) {
+ (void) fprintf(stderr, "ERROR: remove current controller\n");
+ }
+
+ if (cp->disks != NULL && cp->disks[0] != NULL) {
+ (void) fprintf(stderr,
+ "ERROR: remove controller with disk ptrs\n");
+ }
+
+ if (cp->paths != NULL && cp->paths[0] != NULL) {
+ (void) fprintf(stderr,
+ "ERROR: remove controller with path ptrs\n");
+ }
+ }
+
+ return (1);
+}
+
+/*
+ * If we have a controller in the list that is really a path then we need to
+ * take that controller out of the list since nodes that are paths are not
+ * considered to be controllers.
+ */
+static void
+clean_paths(struct search_args *args)
+{
+ controller_t *cp;
+
+ cp = args->controller_listp;
+ while (cp != NULL) {
+ path_t **pp;
+
+ pp = cp->paths;
+ if (pp != NULL) {
+ int i;
+
+ for (i = 0; pp[i]; i++) {
+ remove_invalid_controller(pp[i]->name, cp, args);
+ }
+ }
+ cp = cp->next;
+ }
+}
+
+static disk_t *
+create_disk(char *deviceid, char *kernel_name, struct search_args *args)
+{
+ disk_t *diskp;
+ char *type;
+ char *prod_id;
+ char *vendor_id;
+
+ if (dm_debug) {
+ (void) fprintf(stderr, "INFO: create_disk %s\n", kernel_name);
+ }
+
+ diskp = calloc(1, sizeof (disk_t));
+ if (diskp == NULL) {
+ return (NULL);
+ }
+
+ diskp->controllers = (controller_t **)
+ calloc(1, sizeof (controller_t *));
+ if (diskp->controllers == NULL) {
+ cache_free_disk(diskp);
+ return (NULL);
+ }
+ diskp->controllers[0] = NULL;
+
+ diskp->devid = NULL;
+ if (deviceid != NULL) {
+ if ((diskp->device_id = strdup(deviceid)) == NULL) {
+ cache_free_disk(diskp);
+ return (NULL);
+ }
+
+ (void) devid_str_decode(deviceid, &(diskp->devid), NULL);
+ }
+
+ if (kernel_name != NULL) {
+ diskp->kernel_name = strdup(kernel_name);
+ if (diskp->kernel_name == NULL) {
+ cache_free_disk(diskp);
+ return (NULL);
+ }
+ }
+
+ diskp->paths = NULL;
+ diskp->aliases = NULL;
+
+ diskp->cd_rom = 0;
+ diskp->rpm = 0;
+ type = di_minor_nodetype(args->minor);
+
+ prod_id = get_str_prop(PROD_ID_PROP, args->node);
+ if (prod_id != NULL) {
+ if ((diskp->product_id = strdup(prod_id)) == NULL) {
+ cache_free_disk(diskp);
+ return (NULL);
+ }
+ } else {
+ prod_id = get_str_prop(PROD_ID_USB_PROP, args->node);
+ if (prod_id != NULL) {
+ if ((diskp->product_id = strdup(prod_id)) == NULL) {
+ cache_free_disk(diskp);
+ return (NULL);
+ }
+ }
+ }
+
+ vendor_id = get_str_prop(VENDOR_ID_PROP, args->node);
+ if (vendor_id != NULL) {
+ if ((diskp->vendor_id = strdup(vendor_id)) == NULL) {
+ cache_free_disk(diskp);
+ return (NULL);
+ }
+ } else {
+ vendor_id = get_str_prop(VENDOR_ID_PROP, args->node);
+ if (vendor_id != NULL) {
+ if ((diskp->vendor_id = strdup(vendor_id)) == NULL) {
+ cache_free_disk(diskp);
+ return (NULL);
+ }
+ }
+ }
+
+ /*
+ * DVD, CD-ROM, CD-RW, MO, etc. are all reported as CD-ROMS.
+ * We try to use uscsi later to determine the real type.
+ * The cd_rom flag tells us that the kernel categorized the drive
+ * as a CD-ROM. We leave the drv_type as UKNOWN for now.
+ * The combination of the cd_rom flag being set with the drv_type of
+ * unknown is what triggers the uscsi probe in drive.c.
+ */
+ if (disk_is_cdrom(type)) {
+ diskp->drv_type = DM_DT_UNKNOWN;
+ diskp->cd_rom = 1;
+ diskp->removable = 1;
+ } else if (libdiskmgt_str_eq(type, DDI_NT_FD)) {
+ diskp->drv_type = DM_DT_FLOPPY;
+ diskp->removable = 1;
+ } else {
+ /* not a "CD-ROM" or Floppy */
+
+ diskp->removable = get_prop(REMOVABLE_PROP, args->node);
+ if (diskp->removable == -1) {
+ diskp->removable = 0;
+#ifdef i386
+ /*
+ * x86 does not have removable property. Check for common
+ * removable drives, zip & jaz, and mark those correctly.
+ */
+ if (vendor_id != NULL && prod_id != NULL) {
+ if (str_case_index(vendor_id, "iomega") != NULL) {
+ if (str_case_index(prod_id, "jaz") != NULL) {
+ diskp->removable = 1;
+ } else if (str_case_index(prod_id, "zip") != NULL) {
+ diskp->removable = 1;
+ }
+ }
+ }
+#endif
+ }
+
+ if (diskp->removable) {
+ /*
+ * For removable jaz or zip drives there is no way
+ * to get the drive type unless media is inserted, so we
+ * look at the product-id for a hint.
+ */
+
+ diskp->drv_type = DM_DT_UNKNOWN;
+
+ if (prod_id != NULL) {
+ if (str_case_index(prod_id, "jaz") != NULL) {
+ diskp->drv_type = DM_DT_JAZ;
+ } else if (str_case_index(prod_id, "zip") != NULL) {
+ diskp->drv_type = DM_DT_ZIP;
+ }
+ }
+ } else {
+ diskp->drv_type = DM_DT_FIXED;
+ }
+ }
+ diskp->volm_path_set = 0;
+ diskp->volm_path = NULL;
+
+ diskp->next = args->disk_listp;
+ args->disk_listp = diskp;
+
+ return (diskp);
+}
+
+static char *
+ctype(di_node_t node, di_minor_t minor)
+{
+ char *type;
+ char *name;
+
+ type = di_minor_nodetype(minor);
+ name = di_node_name(node);
+
+ /* IDE disks use SCSI nexus as the type, so handle this special case */
+ if (libdiskmgt_str_eq(name, "ide")) {
+ return (DM_CTYPE_ATA);
+ }
+
+ if (libdiskmgt_str_eq(di_minor_name(minor), "scsa2usb")) {
+ return (DM_CTYPE_USB);
+ }
+
+ if (libdiskmgt_str_eq(type, DDI_NT_SCSI_NEXUS) ||
+ libdiskmgt_str_eq(type, DDI_NT_SCSI_ATTACHMENT_POINT)) {
+ return (DM_CTYPE_SCSI);
+ }
+
+ if (libdiskmgt_str_eq(type, DDI_NT_FC_ATTACHMENT_POINT)) {
+ return (DM_CTYPE_FIBRE);
+ }
+
+ if (libdiskmgt_str_eq(type, DDI_NT_NEXUS) &&
+ libdiskmgt_str_eq(name, "fp")) {
+ return (DM_CTYPE_FIBRE);
+ }
+
+ if (libdiskmgt_str_eq(type, DDI_PSEUDO) &&
+ libdiskmgt_str_eq(name, "ide")) {
+ return (DM_CTYPE_ATA);
+ }
+
+ return (DM_CTYPE_UNKNOWN);
+}
+
+static boolean_t
+disk_is_cdrom(char *type)
+{
+
+ int type_index;
+
+ for (type_index = 0; cdromtypes[type_index] != NULL; type_index++) {
+ if (libdiskmgt_str_eq(type, cdromtypes[type_index])) {
+ return (B_TRUE);
+ }
+ }
+
+ return (B_FALSE);
+}
+
+static alias_t *
+find_alias(disk_t *diskp, char *kernel_name)
+{
+ alias_t *ap;
+
+ ap = diskp->aliases;
+ while (ap != NULL) {
+ if (libdiskmgt_str_eq(ap->kstat_name, kernel_name)) {
+ return (ap);
+ }
+ ap = ap->next;
+ }
+
+ return (NULL);
+}
+
+static bus_t *
+find_bus(struct search_args *args, char *name)
+{
+ bus_t *listp;
+
+ listp = args->bus_listp;
+ while (listp != NULL) {
+ if (libdiskmgt_str_eq(listp->name, name)) {
+ return (listp);
+ }
+
+ listp = listp->next;
+ }
+
+ return (NULL);
+}
+
+static controller_t *
+find_controller(struct search_args *args, char *name)
+{
+ controller_t *listp;
+
+ listp = args->controller_listp;
+ while (listp != NULL) {
+ if (libdiskmgt_str_eq(listp->name, name)) {
+ return (listp);
+ }
+
+ listp = listp->next;
+ }
+
+ return (NULL);
+}
+
+static int
+fix_cluster_devpath(di_devlink_t devlink, void *arg)
+{
+ int fd;
+ struct search_args *args;
+ char *devlink_path;
+ disk_t *diskp = NULL;
+ alias_t *ap = NULL;
+
+ /*
+ * The devlink_path is of the form /dev/did/rdsk/d1s0.
+ */
+
+ args = (struct search_args *)arg;
+
+ /* Find the disk by the deviceid we read from the cluster disk. */
+ devlink_path = (char *)di_devlink_path(devlink);
+ if (devlink_path == NULL) {
+ return (DI_WALK_CONTINUE);
+ }
+
+ if ((fd = open(devlink_path, O_RDONLY|O_NDELAY)) >= 0) {
+ ddi_devid_t devid;
+
+ if (dm_debug > 1) {
+ (void) fprintf(stderr, "INFO: cluster devpath %s\n",
+ devlink_path);
+ }
+
+ if (devid_get(fd, &devid) == 0) {
+ char *minor;
+ char *devidstr;
+
+ minor = di_minor_name(args->minor);
+
+ if ((devidstr = devid_str_encode(devid, minor)) != NULL) {
+ diskp = get_disk_by_deviceid(args->disk_listp, devidstr);
+
+ /*
+ * This really shouldn't happen, since we should have
+ * found all of the disks during our first pass through
+ * the dev tree, but just in case...
+ */
+ if (diskp == NULL) {
+ if (dm_debug > 1) {
+ (void) fprintf(stderr,
+ "INFO: cluster create disk\n");
+ }
+
+ diskp = create_disk(devidstr, NULL, args);
+ if (diskp == NULL) {
+ args->dev_walk_status = ENOMEM;
+ }
+
+ /* add the controller relationship */
+ if (args->dev_walk_status == 0) {
+ if (add_disk2controller(diskp, args) != 0) {
+ args->dev_walk_status = ENOMEM;
+ }
+ }
+
+ if (new_alias(diskp, NULL, devlink_path, args)
+ != 0) {
+ args->dev_walk_status = ENOMEM;
+ }
+ }
+
+ devid_str_free(devidstr);
+ }
+
+ devid_free(devid);
+ }
+ (void) close(fd);
+ }
+
+
+ if (diskp != NULL) {
+ if (dm_debug > 1) {
+ (void) fprintf(stderr, "INFO: cluster found disk\n");
+ }
+
+ ap = diskp->aliases;
+ }
+
+ if (ap != NULL) {
+ /* NOTE: if ap->next != NULL have cluster disks w/ multiple paths */
+
+ if (!ap->cluster) {
+ char *basep;
+ char *namep;
+ int cnt = 0;
+ int size;
+ char alias[MAXPATHLEN];
+
+ /*
+ * First time; save the /dev/rdsk devpaths and update the
+ * alias info with the new alias name.
+ */
+ ap->orig_paths = ap->devpaths;
+ ap->devpaths = NULL;
+
+ free(ap->alias);
+
+ /* get the new cluster alias name */
+ basep = strrchr(devlink_path, '/');
+ if (basep == NULL) {
+ basep = devlink_path;
+ } else {
+ basep++;
+ }
+
+ size = sizeof (alias) - 1;
+ namep = alias;
+ while (*basep != 0 && *basep != 's' && cnt < size) {
+ *namep++ = *basep++;
+ cnt++;
+ }
+ *namep = 0;
+
+ if ((ap->alias = strdup(alias)) == NULL) {
+ args->dev_walk_status = ENOMEM;
+ }
+
+ ap->cluster = 1;
+ }
+
+ if (new_devpath(ap, devlink_path) != 0) {
+ args->dev_walk_status = ENOMEM;
+ }
+ }
+
+ return (DI_WALK_CONTINUE);
+}
+
+/*
+ * Check if we have the drive in our list, based upon the device id.
+ * We got the device id from the dev tree walk. This is encoded
+ * using devid_str_encode(3DEVID). In order to check the device ids we need
+ * to use the devid_compare(3DEVID) function, so we need to decode the
+ * string representation of the device id.
+ */
+static disk_t *
+get_disk_by_deviceid(disk_t *listp, char *devidstr)
+{
+ ddi_devid_t devid;
+
+ if (devidstr == NULL || devid_str_decode(devidstr, &devid, NULL) != 0) {
+ return (NULL);
+ }
+
+ while (listp != NULL) {
+ if (listp->devid != NULL &&
+ devid_compare(listp->devid, devid) == 0) {
+ break;
+ }
+
+ listp = listp->next;
+ }
+
+ devid_free(devid);
+
+ return (listp);
+}
+
+/*
+ * Get the base disk name with no path prefix and no slice (if there is one).
+ * The name parameter should be big enough to hold the name.
+ * This handles diskette names ok (/dev/rdiskette0) since there is no slice,
+ * and converts the raw diskette name.
+ * But, we don't know how to strip off the slice from third party drive
+ * names. That just means that their drive name will include a slice on
+ * it.
+ */
+static void
+get_disk_name_from_path(char *path, char *name, int size)
+{
+ char *basep;
+ int cnt = 0;
+
+ basep = strrchr(path, '/');
+ if (basep == NULL) {
+ basep = path;
+ } else {
+ basep++;
+ }
+
+ size = size - 1; /* leave room for terminating 0 */
+
+ if (is_ctds(basep)) {
+ while (*basep != 0 && *basep != 's' && cnt < size) {
+ *name++ = *basep++;
+ cnt++;
+ }
+ *name = 0;
+ } else {
+ if (strncmp(basep, FLOPPY_NAME, sizeof (FLOPPY_NAME) - 1) == 0) {
+ /*
+ * a floppy, convert rdiskette name to diskette name,
+ * by skipping over the 'r' for raw diskette
+ */
+ basep++;
+ }
+
+ /* not a ctds name, just copy it */
+ (void) strlcpy(name, basep, size);
+ }
+}
+
+static char *
+get_byte_prop(char *prop_name, di_node_t node)
+{
+ int cnt;
+ uchar_t *bytes;
+ int i;
+ char str[MAXPATHLEN];
+
+ cnt = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, prop_name, &bytes);
+ if (cnt < 1) {
+ return (NULL);
+ }
+
+ str[0] = 0;
+ for (i = 0; i < cnt; i++) {
+ char bstr[8]; /* a byte is only 2 hex chars + null */
+
+ (void) snprintf(bstr, sizeof (bstr), "%.2x", bytes[i]);
+ (void) strlcat(str, bstr, sizeof (str));
+ }
+ return (strdup(str));
+}
+
+static di_node_t
+get_parent_bus(di_node_t node, struct search_args *args)
+{
+ di_node_t pnode;
+
+ pnode = di_parent_node(node);
+ if (pnode == DI_NODE_NIL) {
+ return (NULL);
+ }
+
+ if (bus_type(pnode, di_minor_next(pnode, NULL), args->ph) != NULL) {
+ return (pnode);
+ }
+
+ return (get_parent_bus(pnode, args));
+}
+
+static int
+get_prom_int(char *prop_name, di_node_t node, di_prom_handle_t ph)
+{
+ int *n;
+
+ if (di_prom_prop_lookup_ints(ph, node, prop_name, &n) == 1) {
+ return (*n);
+ }
+
+ return (0);
+}
+
+static char *
+get_prom_str(char *prop_name, di_node_t node, di_prom_handle_t ph)
+{
+ char *str;
+
+ if (di_prom_prop_lookup_strings(ph, node, prop_name, &str) == 1) {
+ return (str);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Get one of the positive int or boolean properties.
+ */
+static int
+get_prop(char *prop_name, di_node_t node)
+{
+ int num;
+ int *ip;
+
+ if ((num = di_prop_lookup_ints(DDI_DEV_T_ANY, node, prop_name, &ip))
+ >= 0) {
+ if (num == 0) {
+ /* boolean */
+ return (1);
+ } else if (num == 1) {
+ /* single int */
+ return (*ip);
+ }
+ }
+
+ return (-1);
+}
+
+static char *
+get_str_prop(char *prop_name, di_node_t node)
+{
+ char *str;
+
+ if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, prop_name, &str) == 1) {
+ return (str);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Check if we have the drive in our list, based upon the device id, if the
+ * drive has a device id, or the kernel name, if it doesn't have a device id.
+ */
+static int
+have_disk(struct search_args *args, char *devidstr, char *kernel_name,
+ disk_t **diskp)
+{
+ disk_t *listp;
+
+ *diskp = NULL;
+ listp = args->disk_listp;
+ if (devidstr != NULL) {
+ if ((*diskp = get_disk_by_deviceid(listp, devidstr)) != NULL) {
+ return (1);
+ }
+
+ } else {
+ /* no devid, try matching the kernel names on the drives */
+ while (listp != NULL) {
+ if (libdiskmgt_str_eq(kernel_name, listp->kernel_name)) {
+ *diskp = listp;
+ return (1);
+ }
+ listp = listp->next;
+ }
+ }
+
+ return (0);
+}
+
+static char *
+bus_type(di_node_t node, di_minor_t minor, di_prom_handle_t ph)
+{
+ char *type;
+ int i;
+
+ type = get_prom_str("device_type", node, ph);
+ if (type == NULL) {
+ type = di_node_name(node);
+ }
+
+ for (i = 0; bustypes[i]; i++) {
+ if (libdiskmgt_str_eq(type, bustypes[i])) {
+ return (type);
+ }
+ }
+
+ if (minor != NULL && strcmp(di_minor_nodetype(minor),
+ DDI_NT_USB_ATTACHMENT_POINT) == 0) {
+ return ("usb");
+ }
+
+ return (NULL);
+}
+
+static int
+is_cluster_disk(di_node_t node, di_minor_t minor)
+{
+ if (di_minor_spectype(minor) == S_IFCHR &&
+ libdiskmgt_str_eq(di_minor_nodetype(minor), DDI_PSEUDO) &&
+ libdiskmgt_str_eq(di_node_name(node), CLUSTER_DEV)) {
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * If the input name is in c[t]ds format then return 1, otherwise return 0.
+ */
+static int
+is_ctds(char *name)
+{
+ char *p;
+
+ p = name;
+
+ if (*p++ != 'c') {
+ return (0);
+ }
+ /* skip controller digits */
+ while (isdigit(*p)) {
+ p++;
+ }
+
+ /* handle optional target */
+ if (*p == 't') {
+ p++;
+ /* skip over target */
+ while (isdigit(*p) || isupper(*p)) {
+ p++;
+ }
+ }
+
+ if (*p++ != 'd') {
+ return (0);
+ }
+ while (isdigit(*p)) {
+ p++;
+ }
+
+ if (*p++ != 's') {
+ return (0);
+ }
+
+ /* check the slice number */
+ while (isdigit(*p)) {
+ p++;
+ }
+
+ if (*p != 0) {
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+is_drive(di_minor_t minor)
+{
+ char *type;
+ int type_index;
+
+ type = di_minor_nodetype(minor);
+ type_index = 0;
+
+ while (disktypes[type_index] != NULL) {
+ if (libdiskmgt_str_eq(type, disktypes[type_index])) {
+ return (1);
+ }
+ type_index++;
+ }
+
+ return (0);
+}
+
+static int
+is_HBA(di_node_t node, di_minor_t minor)
+{
+ char *type;
+ char *name;
+ int type_index;
+
+ type = di_minor_nodetype(minor);
+ type_index = 0;
+
+ while (ctrltypes[type_index] != NULL) {
+ if (libdiskmgt_str_eq(type, ctrltypes[type_index])) {
+ return (1);
+ }
+ type_index++;
+ }
+
+ name = di_node_name(node);
+ if (libdiskmgt_str_eq(type, DDI_PSEUDO) &&
+ libdiskmgt_str_eq(name, "ide")) {
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+new_alias(disk_t *diskp, char *kernel_name, char *devlink_path,
+ struct search_args *args)
+{
+ alias_t *aliasp;
+ char alias[MAXPATHLEN];
+ di_node_t pnode;
+
+ aliasp = malloc(sizeof (alias_t));
+ if (aliasp == NULL) {
+ return (ENOMEM);
+ }
+
+ aliasp->alias = NULL;
+ aliasp->kstat_name = NULL;
+ aliasp->wwn = NULL;
+ aliasp->devpaths = NULL;
+ aliasp->orig_paths = NULL;
+
+ get_disk_name_from_path(devlink_path, alias, sizeof (alias));
+
+ aliasp->alias = strdup(alias);
+ if (aliasp->alias == NULL) {
+ cache_free_alias(aliasp);
+ return (ENOMEM);
+ }
+
+ if (kernel_name != NULL) {
+ aliasp->kstat_name = strdup(kernel_name);
+ if (aliasp->kstat_name == NULL) {
+ cache_free_alias(aliasp);
+ return (ENOMEM);
+ }
+ } else {
+ aliasp->kstat_name = NULL;
+ }
+
+ aliasp->cluster = 0;
+ aliasp->lun = get_prop(DM_LUN, args->node);
+ aliasp->target = get_prop(DM_TARGET, args->node);
+ aliasp->wwn = get_byte_prop(WWN_PROP, args->node);
+
+ pnode = di_parent_node(args->node);
+ if (pnode != DI_NODE_NIL) {
+ char prop_name[MAXPROPLEN];
+
+ (void) snprintf(prop_name, sizeof (prop_name),
+ "target%d-sync-speed", aliasp->target);
+ diskp->sync_speed = get_prop(prop_name, pnode);
+ (void) snprintf(prop_name, sizeof (prop_name), "target%d-wide",
+ aliasp->target);
+ diskp->wide = get_prop(prop_name, pnode);
+ }
+
+ if (new_devpath(aliasp, devlink_path) != 0) {
+ cache_free_alias(aliasp);
+ return (ENOMEM);
+ }
+
+ aliasp->next = diskp->aliases;
+ diskp->aliases = aliasp;
+
+ return (0);
+}
+
+/*
+ * Append the new devpath to the end of the devpath list. This is important
+ * since we may want to use the order of the devpaths to match up the vtoc
+ * entries.
+ */
+static int
+new_devpath(alias_t *ap, char *devpath)
+{
+ slice_t *newdp;
+ slice_t *alistp;
+
+ /*
+ * First, search the alias list to be sure that this devpath is
+ * not already there.
+ */
+
+ for (alistp = ap->devpaths; alistp != NULL; alistp = alistp->next) {
+ if (libdiskmgt_str_eq(alistp->devpath, devpath)) {
+ return (0);
+ }
+ }
+
+ /*
+ * Otherwise, not found so add this new devpath to the list.
+ */
+
+ newdp = malloc(sizeof (slice_t));
+ if (newdp == NULL) {
+ return (ENOMEM);
+ }
+
+ newdp->devpath = strdup(devpath);
+ if (newdp->devpath == NULL) {
+ free(newdp);
+ return (ENOMEM);
+ }
+ newdp->slice_num = -1;
+ newdp->next = NULL;
+
+ if (ap->devpaths == NULL) {
+ ap->devpaths = newdp;
+ } else {
+ /* append the devpath to the end of the list */
+ slice_t *dp;
+
+ dp = ap->devpaths;
+ while (dp->next != NULL) {
+ dp = dp->next;
+ }
+
+ dp->next = newdp;
+ }
+
+ return (0);
+}
+
+static path_t *
+new_path(controller_t *cp, disk_t *dp, di_node_t node, di_path_state_t st,
+ char *wwn)
+{
+ char *devpath;
+ path_t *pp;
+ di_minor_t minor;
+
+ /* Special handling for fp attachment node. */
+ if (strcmp(di_node_name(node), "fp") == 0) {
+ di_node_t pnode;
+
+ pnode = di_parent_node(node);
+ if (pnode != DI_NODE_NIL) {
+ node = pnode;
+ }
+ }
+
+ devpath = di_devfs_path(node);
+
+ /* check if the path is already there */
+ pp = NULL;
+ if (cp->paths != NULL) {
+ int i;
+
+ for (i = 0; cp->paths[i]; i++) {
+ if (libdiskmgt_str_eq(devpath, cp->paths[i]->name)) {
+ pp = cp->paths[i];
+ break;
+ }
+ }
+ }
+
+ if (pp != NULL) {
+ /* the path exists, add this disk to it */
+
+ di_devfs_path_free((void *) devpath);
+
+ if (!add_disk2path(dp, pp, st, wwn)) {
+ return (NULL);
+ }
+
+ return (pp);
+ }
+
+ /* create a new path */
+
+ pp = calloc(1, sizeof (path_t));
+ if (pp == NULL) {
+ di_devfs_path_free((void *) devpath);
+ return (NULL);
+ }
+
+ pp->name = strdup(devpath);
+ di_devfs_path_free((void *) devpath);
+ if (pp->name == NULL) {
+ cache_free_path(pp);
+ return (NULL);
+ }
+
+ /* add the disk to the path */
+ if (!add_disk2path(dp, pp, st, wwn)) {
+ return (NULL);
+ }
+
+ /* add the path to the controller */
+ if (add_ptr2array(pp, (void ***)&cp->paths) != 0) {
+ cache_free_path(pp);
+ return (NULL);
+ }
+
+ /* add the controller to the path */
+ pp->controller = cp;
+
+ minor = di_minor_next(node, NULL);
+ if (minor != NULL) {
+ pp->ctype = ctype(node, minor);
+ } else {
+ pp->ctype = DM_CTYPE_UNKNOWN;
+ }
+
+ return (pp);
+}
+
+/*
+ * We pass in the current controller pointer (currp) so we can double check
+ * that we aren't corrupting the list by removing the element we are on. This
+ * should never happen, but it doesn't hurt to double check.
+ */
+static void
+remove_invalid_controller(char *name, controller_t *currp,
+ struct search_args *args)
+{
+ controller_t *cp;
+ bus_t *bp;
+ controller_t *prevp;
+
+ bp = args->bus_listp;
+ while (bp != NULL) {
+ int i;
+
+ for (i = 0; bp->controllers[i]; i++) {
+ if (libdiskmgt_str_eq(bp->controllers[i]->name, name)) {
+ int j;
+
+ /* remove pointer to invalid controller (it is a path) */
+ for (j = i; bp->controllers[j]; j++) {
+ bp->controllers[j] = bp->controllers[j + 1];
+ }
+ }
+ }
+ bp = bp->next;
+ }
+
+ if (args->controller_listp == NULL) {
+ return;
+ }
+
+ cp = args->controller_listp;
+ if (libdiskmgt_str_eq(cp->name, name)) {
+ if (can_remove_controller(cp, currp)) {
+ args->controller_listp = cp->next;
+ cache_free_controller(cp);
+ }
+ return;
+ }
+
+ prevp = cp;
+ cp = cp->next;
+ while (cp != NULL) {
+ if (libdiskmgt_str_eq(cp->name, name)) {
+ if (can_remove_controller(cp, currp)) {
+ prevp->next = cp->next;
+ cache_free_controller(cp);
+ }
+ return;
+ }
+ prevp = cp;
+ cp = cp->next;
+ }
+}
+
+/*
+ * This is the standard strstr code modified for case independence.
+ */
+static char *
+str_case_index(register char *s1, register char *s2)
+{
+ uint_t s2len = strlen(s2); /* length of the second string */
+
+ /* If the length of the second string is 0, return the first arg. */
+ if (s2len == 0) {
+ return (s1);
+ }
+
+ while (strlen(s1) >= s2len) {
+ if (strncasecmp(s1, s2, s2len) == 0) {
+ return (s1);
+ }
+ s1++;
+ }
+ return (NULL);
+}
diff --git a/usr/src/lib/libdiskmgt/common/inuse_dump.c b/usr/src/lib/libdiskmgt/common/inuse_dump.c
new file mode 100644
index 0000000000..7725f42a70
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/inuse_dump.c
@@ -0,0 +1,96 @@
+/*
+ * 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"
+
+/*
+ * Checks for a match with the the dump slice.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <synch.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/dumpadm.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+
+/* Cached file descriptor for /dev/dump. */
+static int dump_fd = -1;
+
+static mutex_t dump_lock = DEFAULTMUTEX;
+
+/*
+ * Check the dump device against the input slice.
+ */
+int
+inuse_dump(char *slice, nvlist_t *attrs, int *errp)
+{
+ int found = 0;
+ int fd;
+ char device[MAXPATHLEN];
+
+ *errp = 0;
+ if (slice == NULL) {
+ return (found);
+ }
+
+ /*
+ * We only want to open /dev/dump once instead of for every
+ * slice so we cache the open file descriptor. The ioctl
+ * is cheap so we can do that for every slice.
+ */
+ (void) mutex_lock(&dump_lock);
+
+ if (dump_fd == -1) {
+ if ((dump_fd = open("/dev/dump", O_RDONLY)) >= 0)
+ (void) fcntl(dump_fd, F_SETFD, FD_CLOEXEC);
+ }
+
+ fd = dump_fd;
+
+ (void) mutex_unlock(&dump_lock);
+
+ if (fd != -1) {
+ if (ioctl(fd, DIOCGETDEV, device) != -1) {
+ if (strcmp(slice, device) == 0) {
+ libdiskmgt_add_str(attrs, DM_USED_BY,
+ DM_USE_DUMP, errp);
+ libdiskmgt_add_str(attrs, DM_USED_NAME,
+ DM_USE_DUMP, errp);
+ found = 1;
+ }
+ }
+ }
+
+ return (found);
+}
diff --git a/usr/src/lib/libdiskmgt/common/inuse_fs.c b/usr/src/lib/libdiskmgt/common/inuse_fs.c
new file mode 100644
index 0000000000..7b37beb8c2
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/inuse_fs.c
@@ -0,0 +1,344 @@
+/*
+ * 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 <dirent.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <synch.h>
+#include <unistd.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/vfstab.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/fs/ufs_fs.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+
+/*
+ * The list of filesystem heuristic programs.
+ */
+struct heuristic {
+ struct heuristic *next;
+ char *prog;
+ char *type;
+};
+
+struct vfstab_list {
+ char *special;
+ char *mountp;
+ struct vfstab_list *next;
+};
+
+static struct vfstab_list *vfstab_listp = NULL;
+static mutex_t vfstab_lock = DEFAULTMUTEX;
+
+static time_t timestamp = 0;
+
+static struct heuristic *hlist = NULL;
+static int initialized = 0;
+static mutex_t init_lock = DEFAULTMUTEX;
+
+static int has_fs(char *prog, char *slice);
+static int load_heuristics();
+static int add_use_record(struct vfstab *vp);
+static int load_vfstab();
+static void free_vfstab(struct vfstab_list *listp);
+
+/*
+ * Use the heuristics to check for a filesystem on the slice.
+ */
+int
+inuse_fs(char *slice, nvlist_t *attrs, int *errp)
+{
+ struct heuristic *hp;
+ time_t curr_time;
+ int found = 0;
+
+
+ *errp = 0;
+
+ if (slice == NULL) {
+ return (0);
+ }
+
+ /*
+ * We get the list of heuristic programs one time.
+ */
+ (void) mutex_lock(&init_lock);
+ if (!initialized) {
+ *errp = load_heuristics();
+
+ if (*errp == 0) {
+ initialized = 1;
+ }
+ }
+ (void) mutex_unlock(&init_lock);
+
+ /* Run each of the heuristics. */
+ for (hp = hlist; hp; hp = hp->next) {
+ if (has_fs(hp->prog, slice)) {
+ libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_FS, errp);
+ libdiskmgt_add_str(attrs, DM_USED_NAME, hp->type, errp);
+ found = 1;
+ }
+ }
+
+ if (*errp != 0)
+ return (found);
+
+ /*
+ * Second heuristic used is the check for an entry in vfstab
+ */
+
+ (void) mutex_lock(&vfstab_lock);
+ curr_time = time(NULL);
+
+ if (timestamp < curr_time && (curr_time - timestamp) > 60) {
+ free_vfstab(vfstab_listp);
+ *errp = load_vfstab();
+ timestamp = curr_time;
+ }
+
+ if (*errp == 0) {
+ struct vfstab_list *listp;
+ listp = vfstab_listp;
+
+ while (listp != NULL) {
+ if (strcmp(slice, listp->special) == 0) {
+ char *mountp = "";
+
+ if (listp->mountp != NULL)
+ mountp = listp->mountp;
+
+ libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_VFSTAB, errp);
+ libdiskmgt_add_str(attrs, DM_USED_NAME, mountp, errp);
+ found = 1;
+ break;
+ }
+ listp = listp->next;
+ }
+ }
+ (void) mutex_unlock(&vfstab_lock);
+ return (found);
+}
+
+static int
+has_fs(char *prog, char *slice)
+{
+ pid_t pid;
+ int loc;
+ mode_t mode = S_IRUSR | S_IWUSR;
+
+ switch ((pid = fork1())) {
+ case 0:
+ /* child process */
+
+ closefrom(1);
+ (void) open("/dev/null", O_WRONLY, mode);
+ (void) open("/dev/null", O_WRONLY, mode);
+ (void) execl(prog, "fstyp", slice, NULL);
+ _exit(1);
+ break;
+
+ case -1:
+ return (0);
+
+ default:
+ /* parent process */
+ break;
+ }
+
+ (void) waitpid(pid, &loc, 0);
+
+ if (WIFEXITED(loc) && WEXITSTATUS(loc) == 0) {
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Create a list of filesystem heuristic programs.
+ */
+static int
+load_heuristics()
+{
+ DIR *dirp;
+
+ if ((dirp = opendir("/usr/lib/fs")) != NULL) {
+ struct dirent *dp;
+
+ while ((dp = readdir(dirp)) != NULL) {
+ char path[MAXPATHLEN];
+ struct stat buf;
+ DIR *subdirp;
+
+ /* skip known dirs */
+ if (strcmp(dp->d_name, ".") == 0 ||
+ strcmp(dp->d_name, "..") == 0) {
+ continue;
+ }
+
+ (void) snprintf(path, sizeof (path), "/usr/lib/fs/%s",
+ dp->d_name);
+
+ if (stat(path, &buf) != 0 || !(buf.st_mode & S_IFDIR)) {
+ continue;
+ }
+
+ if ((subdirp = opendir(path)) != NULL) {
+ struct dirent *sdp;
+
+ while ((sdp = readdir(subdirp)) != NULL) {
+
+ if (strcmp(sdp->d_name, "fstyp") == 0) {
+ char progpath[MAXPATHLEN];
+
+ (void) snprintf(progpath, sizeof (progpath),
+ "/usr/lib/fs/%s/fstyp", dp->d_name);
+
+ if (stat(progpath, &buf) == 0 &&
+ buf.st_mode & S_IFREG) {
+
+ struct heuristic *hp;
+
+ hp = (struct heuristic *)
+ malloc(sizeof (struct heuristic));
+
+ if (hp == NULL) {
+ (void) closedir(subdirp);
+ (void) closedir(dirp);
+ return (ENOMEM);
+ }
+
+ if ((hp->prog = strdup(progpath)) == NULL) {
+ (void) closedir(subdirp);
+ (void) closedir(dirp);
+ return (ENOMEM);
+ }
+
+ if ((hp->type = strdup(dp->d_name)) == NULL) {
+ (void) closedir(subdirp);
+ (void) closedir(dirp);
+ return (ENOMEM);
+ }
+
+ hp->next = hlist;
+ hlist = hp;
+ }
+
+ break;
+ }
+ }
+
+ (void) closedir(subdirp);
+ }
+ }
+
+ (void) closedir(dirp);
+ }
+
+ return (0);
+}
+
+static int
+load_vfstab()
+{
+ FILE *fp;
+ struct vfstab vp;
+ int status = 1;
+
+ fp = fopen(VFSTAB, "r");
+ if (fp != NULL) {
+ (void) memset(&vp, 0, sizeof (struct vfstab));
+ while (getvfsent(fp, &vp) == 0) {
+ status = add_use_record(&vp);
+ if (status != 0) {
+ (void) fclose(fp);
+ return (status);
+ }
+ (void) memset(&vp, 0, sizeof (struct vfstab));
+ }
+ (void) fclose(fp);
+ status = 0;
+ }
+
+ return (status);
+}
+
+static int
+add_use_record(struct vfstab *vp)
+{
+ struct vfstab_list *vfsp;
+
+ vfsp = (struct vfstab_list *)malloc(sizeof (struct vfstab_list));
+ if (vfsp == NULL) {
+ return (ENOMEM);
+ }
+
+ vfsp->special = strdup(vp->vfs_special);
+ if (vfsp->special == NULL) {
+ free(vfsp);
+ return (ENOMEM);
+ }
+
+ if (vp->vfs_mountp != NULL) {
+ vfsp->mountp = strdup(vp->vfs_mountp);
+ if (vfsp->mountp == NULL) {
+ free(vfsp);
+ return (ENOMEM);
+ }
+ } else {
+ vfsp->mountp = NULL;
+ }
+
+ vfsp->next = vfstab_listp;
+ vfstab_listp = vfsp;
+
+ return (0);
+}
+
+static void
+free_vfstab(struct vfstab_list *listp)
+{
+ struct vfstab_list *nextp;
+
+ while (listp != NULL) {
+ nextp = listp->next;
+ free((void *)listp->special);
+ free((void *)listp->mountp);
+ free((void *)listp);
+ listp = nextp;
+ }
+
+ vfstab_listp = NULL;
+}
diff --git a/usr/src/lib/libdiskmgt/common/inuse_lu.c b/usr/src/lib/libdiskmgt/common/inuse_lu.c
new file mode 100644
index 0000000000..48d2089de5
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/inuse_lu.c
@@ -0,0 +1,343 @@
+/*
+ * 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"
+
+/*
+ * Creates and maintains a short-term cache of live upgrade slices.
+ */
+
+#include <dirent.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <synch.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+
+#define TMPNM_SIZE 25
+
+/*
+ * The list of live upgrade slices in use.
+ */
+
+struct lu_list {
+ struct lu_list *next;
+ char *slice;
+ char *name;
+};
+
+static struct lu_list *lu_listp = NULL;
+static time_t timestamp = 0;
+static mutex_t lu_lock = DEFAULTMUTEX;
+
+static int add_use_record(char *devname, char *name);
+static void free_lu(struct lu_list *listp);
+static int load_lu();
+static int lustatus(int fd);
+static int lufslist(int fd);
+static int run_cmd(char *path, char *cmd, char *arg, int fd);
+
+/*
+ * Search the list of devices under live upgrade for the specified device.
+ */
+int
+inuse_lu(char *slice, nvlist_t *attrs, int *errp)
+{
+ int found = 0;
+ time_t curr_time;
+
+ *errp = 0;
+
+ if (slice == NULL) {
+ return (found);
+ }
+
+ /*
+ * We don't want to have to re-read the live upgrade config for
+ * every slice, but we can't just cache it since there is no event
+ * when this changes. So, we'll keep the config in memory for
+ * a short time (1 minute) before reloading it.
+ */
+ (void) mutex_lock(&lu_lock);
+
+ curr_time = time(NULL);
+ if (timestamp < curr_time && (curr_time - timestamp) > 60) {
+ free_lu(lu_listp); /* free old entries */
+ lu_listp = NULL;
+ *errp = load_lu(); /* load the cache */
+ timestamp = curr_time;
+ }
+
+ if (*errp == 0) {
+ struct lu_list *listp;
+
+ listp = lu_listp;
+ while (listp != NULL) {
+ if (strcmp(slice, listp->slice) == 0) {
+ libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_LU, errp);
+ libdiskmgt_add_str(attrs, DM_USED_NAME, listp->name, errp);
+ found = 1;
+ break;
+ }
+ listp = listp->next;
+ }
+ }
+
+ (void) mutex_unlock(&lu_lock);
+
+ return (found);
+}
+
+static int
+add_use_record(char *devname, char *name)
+{
+ struct lu_list *sp;
+
+ sp = (struct lu_list *)malloc(sizeof (struct lu_list));
+ if (sp == NULL) {
+ return (ENOMEM);
+ }
+
+ if ((sp->slice = strdup(devname)) == NULL) {
+ free(sp);
+ return (ENOMEM);
+ }
+
+ if ((sp->name = strdup(name)) == NULL) {
+ free(sp->slice);
+ free(sp);
+ return (ENOMEM);
+ }
+
+ sp->next = lu_listp;
+ lu_listp = sp;
+
+ return (0);
+}
+
+/*
+ * Free the list of liveupgrade entries.
+ */
+static void
+free_lu(struct lu_list *listp) {
+
+ struct lu_list *nextp;
+
+ while (listp != NULL) {
+ nextp = listp->next;
+ free((void *)listp->slice);
+ free((void *)listp->name);
+ free((void *)listp);
+ listp = nextp;
+ }
+}
+
+/*
+ * Create a list of live upgrade devices.
+ */
+static int
+load_lu()
+{
+ char tmpname[TMPNM_SIZE];
+ int fd;
+ int status = 0;
+
+ (void) strlcpy(tmpname, "/var/run/dm_lu_XXXXXX", TMPNM_SIZE);
+ if ((fd = mkstemp(tmpname)) != -1) {
+ (void) unlink(tmpname);
+ if (run_cmd("/usr/sbin/lustatus", "lustatus", NULL, fd)) {
+ status = lustatus(fd);
+ } else {
+ (void) close(fd);
+ }
+ }
+
+ return (status);
+}
+
+/*
+ * The XML generated by the live upgrade commands is not parseable by the
+ * standard Solaris XML parser, so we have to do it ourselves.
+ */
+static int
+lufslist(int fd)
+{
+ FILE *fp;
+ char line[MAXPATHLEN];
+ int status;
+
+ if ((fp = fdopen(fd, "r")) == NULL) {
+ (void) close(fd);
+ return (0);
+ }
+
+ (void) fseek(fp, 0L, SEEK_SET);
+ while (fgets(line, sizeof (line), fp) == line) {
+ char *devp;
+ char *nmp;
+ char *ep;
+
+ if (strncmp(line, "<beFsComponent ", 15) != 0) {
+ continue;
+ }
+
+ if ((devp = strstr(line, "fsDevice=\"")) == NULL) {
+ continue;
+ }
+
+ devp = devp + 10;
+
+ if ((ep = strchr(devp, '"')) == NULL) {
+ continue;
+ }
+
+ *ep = 0;
+
+ /* try to get the mountpoint name */
+ if ((nmp = strstr(ep + 1, "mountPoint=\"")) != NULL) {
+ nmp = nmp + 12;
+
+ if ((ep = strchr(nmp, '"')) != NULL) {
+ *ep = 0;
+ } else {
+ nmp = "";
+ }
+
+ } else {
+ nmp = "";
+ }
+
+ if ((status = add_use_record(devp, nmp)) != 0) {
+ break;
+ }
+ }
+
+ (void) fclose(fp);
+
+ return (status);
+}
+
+static int
+lustatus(int fd)
+{
+ FILE *fp;
+ char line[MAXPATHLEN];
+ int status = 0;
+
+ if ((fp = fdopen(fd, "r")) == NULL) {
+ (void) close(fd);
+ return (0);
+ }
+
+ (void) fseek(fp, 0L, SEEK_SET);
+ while (fgets(line, sizeof (line), fp) == line) {
+ char *sp;
+ char *ep;
+ char tmpname[TMPNM_SIZE];
+ int ffd;
+
+ if (strncmp(line, "<beStatus ", 10) != 0) {
+ continue;
+ }
+
+ if ((sp = strstr(line, "name=\"")) == NULL) {
+ continue;
+ }
+
+ sp = sp + 6;
+
+ if ((ep = strchr(sp, '"')) == NULL) {
+ continue;
+ }
+
+ *ep = 0;
+
+ (void) strlcpy(tmpname, "/var/run/dm_lu_XXXXXX", TMPNM_SIZE);
+ if ((ffd = mkstemp(tmpname)) != -1) {
+ (void) unlink(tmpname);
+
+ if (run_cmd("/usr/sbin/lufslist", "lufslist", sp, ffd) == 0) {
+ (void) close(ffd);
+ break;
+ }
+
+ if ((status = lufslist(ffd)) != 0) {
+ break;
+ }
+ }
+ }
+
+ (void) fclose(fp);
+
+ return (status);
+}
+
+static int
+run_cmd(char *path, char *cmd, char *arg, int fd)
+{
+ pid_t pid;
+ int loc;
+
+ /* create the server process */
+ switch ((pid = fork1())) {
+ case 0:
+ /* child process */
+ (void) close(1);
+ (void) dup(fd);
+ (void) close(2);
+ (void) dup(fd);
+ closefrom(3);
+ (void) execl(path, cmd, "-X", arg, NULL);
+ _exit(1);
+ break;
+
+ case -1:
+ return (0);
+
+ default:
+ /* parent process */
+ break;
+ }
+
+ (void) waitpid(pid, &loc, 0);
+
+ /* printf("got 0x%x %d %d\n", loc, WIFEXITED(loc), WEXITSTATUS(loc)); */
+
+ if (WIFEXITED(loc) && WEXITSTATUS(loc) == 0) {
+ return (1);
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/libdiskmgt/common/inuse_mnt.c b/usr/src/lib/libdiskmgt/common/inuse_mnt.c
new file mode 100644
index 0000000000..54001a46ce
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/inuse_mnt.c
@@ -0,0 +1,417 @@
+/*
+ * 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"
+
+/*
+ * Creates and maintains a cache of mount points.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <synch.h>
+#include <thread.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mnttab.h>
+#include <sys/swap.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+
+/*
+ * The list of mount point entries in /etc/mnttab
+ */
+
+struct mntpnt_list {
+ struct mntpnt_list *next;
+ char *special;
+ char *mountp;
+};
+
+static struct mntpnt_list *mntpoint_listp = NULL;
+static rwlock_t mntpoint_lock = DEFAULTRWLOCK;
+static int initialized = 0;
+static mutex_t init_lock = DEFAULTMUTEX;
+
+static boolean_t diff_mnttab(int send_event, struct mntpnt_list *firstp,
+ struct mntpnt_list *secondp);
+static void free_mnttab(struct mntpnt_list *listp);
+static boolean_t in_list(struct mntpnt_list *elementp,
+ struct mntpnt_list *listp);
+static int load_mnttab(int send_event);
+static void watch_mnttab();
+
+/*
+ * Search the list of devices from /etc/mnttab to find the mount point
+ * for the specified device.
+ */
+int
+inuse_mnt(char *slice, nvlist_t *attrs, int *errp)
+{
+ struct mntpnt_list *listp;
+ int found = 0;
+
+ *errp = 0;
+ if (slice == NULL) {
+ return (found);
+ }
+
+ (void) mutex_lock(&init_lock);
+ if (!initialized) {
+ thread_t mnttab_thread;
+
+ /* load the mntpnt cache */
+ *errp = load_mnttab(B_FALSE);
+
+ if (*errp == 0) {
+ /* start a thread to monitor the mnttab */
+ *errp = thr_create(NULL, NULL, (void *(*)(void *))watch_mnttab,
+ NULL, THR_NEW_LWP | THR_DAEMON, &mnttab_thread);
+ }
+
+ if (*errp == 0) {
+ initialized = 1;
+ }
+ }
+ (void) mutex_unlock(&init_lock);
+
+ (void) rw_rdlock(&mntpoint_lock);
+ listp = mntpoint_listp;
+ while (listp != NULL) {
+ if (libdiskmgt_str_eq(slice, listp->special)) {
+ libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_MOUNT, errp);
+ libdiskmgt_add_str(attrs, DM_USED_NAME, listp->mountp, errp);
+ found = 1;
+ break;
+ }
+ listp = listp->next;
+ }
+ (void) rw_unlock(&mntpoint_lock);
+
+ return (found);
+}
+
+/*
+ * Return true if the lists are different. Send an event for each different
+ * device.
+ */
+static boolean_t
+diff_mnttab(int send_event, struct mntpnt_list *firstp,
+ struct mntpnt_list *secondp)
+{
+ boolean_t different = B_FALSE;
+ struct mntpnt_list *listp;
+
+ listp = firstp;
+ while (listp != NULL) {
+ if (! in_list(listp, secondp)) {
+ /* not in new list, so was mounted and now unmounted */
+ if (send_event) {
+ events_new_slice_event(listp->special, DM_EV_TCHANGE);
+ }
+ different = B_TRUE;
+ }
+ listp = listp->next;
+ }
+
+ listp = secondp;
+ while (listp != NULL) {
+ if (! in_list(listp, firstp)) {
+ /* not in orig list, so this is a new mount */
+ if (send_event) {
+ events_new_slice_event(listp->special, DM_EV_TCHANGE);
+ }
+ different = B_TRUE;
+ }
+ listp = listp->next;
+ }
+
+ return (different);
+}
+
+/*
+ * free_mnttab()
+ *
+ * Free the list of metadevices from /etc/mnttab.
+ */
+static void
+free_mnttab(struct mntpnt_list *listp) {
+
+ struct mntpnt_list *nextp;
+
+ while (listp != NULL) {
+ nextp = listp->next;
+ free((void *)listp->special);
+ free((void *)listp->mountp);
+ free((void *)listp);
+ listp = nextp;
+ }
+}
+
+/*
+ * Return true if the element is in the list.
+ */
+static boolean_t
+in_list(struct mntpnt_list *elementp, struct mntpnt_list *listp)
+{
+ while (listp != NULL) {
+ if (libdiskmgt_str_eq(elementp->special, listp->special) &&
+ libdiskmgt_str_eq(elementp->mountp, listp->mountp)) {
+ return (B_TRUE);
+ }
+ listp = listp->next;
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * load_mnttab()
+ *
+ * Create a list of devices from /etc/mnttab and swap.
+ * return 1 if the list has changed, 0 if the list is still the same
+ */
+static int
+load_mnttab(int send_event)
+{
+
+ struct mntpnt_list *currp;
+ FILE *fp;
+ struct mntpnt_list *headp;
+ int num;
+ struct mntpnt_list *prevp;
+
+ headp = NULL;
+ prevp = NULL;
+
+ /* get the mnttab entries */
+ if ((fp = fopen("/etc/mnttab", "r")) != NULL) {
+
+ struct mnttab entry;
+
+ while (getmntent(fp, &entry) == 0) {
+
+ /*
+ * Ignore entries that are incomplete or that are not
+ * devices (skips network mounts, automounter entries,
+ * /proc, etc.).
+ */
+ if (entry.mnt_special == NULL ||
+ entry.mnt_mountp == NULL ||
+ strncmp(entry.mnt_special, "/dev", 4) != 0) {
+ continue;
+ }
+
+ currp = (struct mntpnt_list *)calloc((size_t)1,
+ (size_t)sizeof (struct mntpnt_list));
+
+ if (currp == NULL) {
+ /*
+ * out of memory, free what we have and return
+ */
+ free_mnttab(headp);
+ (void) fclose(fp);
+ return (ENOMEM);
+ }
+
+ if (headp == NULL) {
+ headp = currp;
+ } else {
+ prevp->next = currp;
+ }
+
+ currp->next = NULL;
+
+ currp->special = strdup(entry.mnt_special);
+ if (currp->special == NULL) {
+ /*
+ * out of memory, free what we have and return
+ */
+ free_mnttab(headp);
+ (void) fclose(fp);
+ return (ENOMEM);
+ }
+
+ currp->mountp = strdup(entry.mnt_mountp);
+ if (currp->mountp == NULL) {
+ /*
+ * out of memory, free what we have and return
+ */
+ free_mnttab(headp);
+ (void) fclose(fp);
+ return (ENOMEM);
+ }
+
+ prevp = currp;
+ }
+
+ (void) fclose(fp);
+ }
+
+ /* get the swap entries */
+ if ((num = swapctl(SC_GETNSWP, NULL)) > -1) {
+
+ struct swaptable *st;
+ struct swapent *swapent;
+ int i;
+ char *path;
+ char *pathstart;
+ char fullpath[MAXPATHLEN+1];
+
+ st = malloc((size_t)((num * sizeof (swapent_t)) + sizeof (int)));
+ if (st == NULL) {
+ /* out of memory, free what we have and return */
+ free_mnttab(headp);
+ return (ENOMEM);
+ }
+
+ path = malloc(num * MAXPATHLEN);
+ if (path == NULL) {
+ /* out of memory, free what we have and return */
+ free(st);
+ free_mnttab(headp);
+ return (ENOMEM);
+ }
+ pathstart = path;
+
+ swapent = st->swt_ent;
+ for (i = 0; i < num; i++, swapent++) {
+ swapent->ste_path = path;
+ path += MAXPATHLEN;
+ }
+
+ st->swt_n = num;
+ if ((num = swapctl(SC_LIST, st)) >= 0) {
+ swapent = st->swt_ent;
+ for (i = 0; i < num; i++, swapent++) {
+
+ currp = (struct mntpnt_list *)
+ calloc((size_t)1, (size_t)sizeof (struct mntpnt_list));
+
+ if (currp == NULL) {
+ /* out of memory, free what we have and return */
+ free((void *)st);
+ free((void *)pathstart);
+ free_mnttab(headp);
+ return (ENOMEM);
+ }
+
+ if (headp == NULL) {
+ headp = currp;
+ } else {
+ prevp->next = currp;
+ }
+
+ currp->next = NULL;
+
+ if (*swapent->ste_path != '/') {
+ (void) snprintf(fullpath, sizeof (fullpath), "/dev/%s",
+ swapent->ste_path);
+ } else {
+ (void) strlcpy(fullpath, swapent->ste_path,
+ sizeof (fullpath));
+ }
+
+ currp->special = strdup(fullpath);
+ if (currp->special == NULL) {
+ /* out of memory, free what we have and return */
+ free(st);
+ free(pathstart);
+ free_mnttab(headp);
+ return (ENOMEM);
+ }
+
+ currp->mountp = strdup("swap");
+ if (currp->mountp == NULL) {
+ /* out of memory, free what we have and return */
+ free(st);
+ free(pathstart);
+ free_mnttab(headp);
+ return (ENOMEM);
+ }
+
+ prevp = currp;
+ }
+ }
+
+ free(st);
+ free(pathstart);
+ }
+
+ /* note that we unlock the mutex in both paths of this if statement */
+ (void) rw_wrlock(&mntpoint_lock);
+ if (diff_mnttab(send_event, mntpoint_listp, headp) == B_TRUE) {
+ struct mntpnt_list *tmpp;
+
+ tmpp = mntpoint_listp;
+ mntpoint_listp = headp;
+ (void) rw_unlock(&mntpoint_lock);
+
+ /* free the old list */
+ free_mnttab(tmpp);
+ } else {
+ (void) rw_unlock(&mntpoint_lock);
+ /* no change that we care about, so keep the current list */
+ free_mnttab(headp);
+ }
+ return (0);
+}
+
+/*
+ * This is a thread that runs forever, watching for changes in the mnttab
+ * that would cause us to flush and reload the cache of mnt entries. Only
+ * changes to /dev devices will cause the cache to be flushed and reloaded.
+ */
+static void
+watch_mnttab()
+{
+ struct pollfd fds[1];
+ int res;
+
+ if ((fds[0].fd = open("/etc/mnttab", O_RDONLY)) != -1) {
+
+ char buf[81];
+
+ /* do the initial read so we don't get the event right away */
+ (void) read(fds[0].fd, buf, (size_t)(sizeof (buf) - 1));
+ (void) lseek(fds[0].fd, 0, SEEK_SET);
+
+ fds[0].events = POLLRDBAND;
+ while (res = poll(fds, (nfds_t)1, -1)) {
+ if (res <= 0)
+ continue;
+
+ (void) load_mnttab(B_TRUE);
+
+ (void) read(fds[0].fd, buf, (size_t)(sizeof (buf) - 1));
+ (void) lseek(fds[0].fd, 0, SEEK_SET);
+ }
+ }
+}
diff --git a/usr/src/lib/libdiskmgt/common/inuse_svm.c b/usr/src/lib/libdiskmgt/common/inuse_svm.c
new file mode 100644
index 0000000000..9298907af3
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/inuse_svm.c
@@ -0,0 +1,639 @@
+/*
+ * 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"
+
+/*
+ * Creates and maintains a cache of slices used by SVM.
+ */
+
+#include <meta.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <synch.h>
+#include <thread.h>
+#include <dlfcn.h>
+#include <link.h>
+#include <libsysevent.h>
+#include <sys/types.h>
+#include <sys/sysevent/eventdefs.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+
+/*
+ * The list of SVM slices in use.
+ */
+
+struct svm_list {
+ struct svm_list *next;
+ char *slice;
+ char *name;
+ char *type;
+};
+
+static struct svm_list *svm_listp = NULL;
+static rwlock_t svm_lock = DEFAULTRWLOCK;
+static int initialized = 0;
+static mutex_t init_lock = DEFAULTMUTEX;
+
+static int add_use_record(char *devname, char *type, char *mname);
+static int diskset_info(mdsetname_t *sp);
+static int drive_in_diskset(char *dpath, char *setname);
+static void event_handler();
+static void free_names(mdnamelist_t *nlp);
+static void free_svm(struct svm_list *listp);
+static int init_svm();
+static int load_svm();
+static int new_entry(char *sname, char *type, char *mname,
+ mdsetname_t *sp);
+
+/*
+ * Pointers to libmeta functions that we dynamically resolve.
+ */
+static set_t (*mdl_get_max_sets)(md_error_t *ep);
+static void (*mdl_mdclrerror)(md_error_t *ep);
+static md_error_t *mdl_mdnullerror;
+static void (*mdl_metaflushnames)(int flush_sr_cache);
+static void (*mdl_metaflushsetname)(mdsetname_t *sp);
+static void (*mdl_metafreenamelist)(mdnamelist_t *nlp);
+static void (*mdl_metafreereplicalist)(md_replicalist_t *rlp);
+static md_drive_desc *(*mdl_metaget_drivedesc)(mdsetname_t *sp, int flags,
+ md_error_t *ep);
+static mdname_t *(*mdl_metaname)(mdsetname_t **spp, char *uname,
+ md_error_t *ep);
+static int (*mdl_metareplicalist)(mdsetname_t *sp, int flags,
+ md_replicalist_t **rlpp, md_error_t *ep);
+static mdsetname_t *(*mdl_metasetnosetname)(set_t setno, md_error_t *ep);
+static int (*mdl_meta_get_hotspare_names)(mdsetname_t *sp,
+ mdnamelist_t **nlpp, int options, md_error_t *ep);
+static md_raid_t *(*mdl_meta_get_raid)(mdsetname_t *sp, mdname_t *raidnp,
+ md_error_t *ep);
+static int (*mdl_meta_get_raid_names)(mdsetname_t *sp,
+ mdnamelist_t **nlpp, int options, md_error_t *ep);
+static md_sp_t *(*mdl_meta_get_sp)(mdsetname_t *sp, mdname_t *np,
+ md_error_t *ep);
+static int (*mdl_meta_get_sp_names)(mdsetname_t *sp,
+ mdnamelist_t **nlpp, int options, md_error_t *ep);
+static md_stripe_t *(*mdl_meta_get_stripe)(mdsetname_t *sp,
+ mdname_t *stripenp, md_error_t *ep);
+static int (*mdl_meta_get_stripe_names)(mdsetname_t *sp,
+ mdnamelist_t **nlpp, int options, md_error_t *ep);
+static int (*mdl_meta_get_trans_names)(mdsetname_t *sp,
+ mdnamelist_t **nlpp, int options, md_error_t *ep);
+static void (*mdl_meta_invalidate_name)(mdname_t *np);
+static void (*mdl_sdssc_bind_library)();
+
+/*
+ * Search the list of devices under SVM for the specified device.
+ */
+int
+inuse_svm(char *slice, nvlist_t *attrs, int *errp)
+{
+ struct svm_list *listp;
+ int found = 0;
+
+ *errp = 0;
+ if (slice == NULL) {
+ return (found);
+ }
+
+ (void) mutex_lock(&init_lock);
+ if (!initialized) {
+ /* dynamically load libmeta */
+ if (init_svm()) {
+ /* need to initialize the cluster library to avoid seg faults */
+ (mdl_sdssc_bind_library)();
+
+ /* load the SVM cache */
+ *errp = load_svm();
+
+ if (*errp == 0) {
+ /* start a thread to monitor the svm config */
+ sysevent_handle_t *shp;
+ const char *subclass_list[1];
+
+ shp = sysevent_bind_handle(event_handler);
+ if (shp != NULL) {
+ subclass_list[0] = EC_SUB_ALL;
+ if (sysevent_subscribe_event(shp, EC_SVM_CONFIG,
+ subclass_list, 1) != 0) {
+ *errp = errno;
+ }
+ } else {
+ *errp = errno;
+ }
+ }
+ }
+
+ if (*errp == 0) {
+ initialized = 1;
+ }
+ }
+ (void) mutex_unlock(&init_lock);
+
+ (void) rw_rdlock(&svm_lock);
+ listp = svm_listp;
+ while (listp != NULL) {
+ if (strcmp(slice, listp->slice) == 0) {
+ libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_SVM, errp);
+ if (strcmp(listp->type, "mdb") == 0 ||
+ strcmp(listp->type, "hs") == 0) {
+
+ libdiskmgt_add_str(attrs, DM_USED_NAME, listp->type, errp);
+ } else {
+ char name[MAXPATHLEN];
+ (void) snprintf(name, MAXPATHLEN, "%s:%s", listp->type,
+ listp->name);
+ libdiskmgt_add_str(attrs, DM_USED_NAME, name, errp);
+ }
+ found = 1;
+ break;
+ }
+ listp = listp->next;
+ }
+ (void) rw_unlock(&svm_lock);
+
+ return (found);
+}
+
+static int
+add_use_record(char *devname, char *type, char *mname)
+{
+ struct svm_list *sp;
+
+ /* If prev. record is a dup, skip it. */
+ if (svm_listp != NULL && strcmp(svm_listp->slice, devname) == 0 &&
+ strcmp(svm_listp->type, type) == 0) {
+ return (0);
+ }
+
+ sp = (struct svm_list *)malloc(sizeof (struct svm_list));
+ if (sp == NULL) {
+ return (ENOMEM);
+ }
+
+ if ((sp->slice = strdup(devname)) == NULL) {
+ free(sp);
+ return (ENOMEM);
+ }
+
+ if ((sp->name = strdup(mname)) == NULL) {
+ free(sp->slice);
+ free(sp);
+ return (ENOMEM);
+ }
+
+ if ((sp->type = strdup(type)) == NULL) {
+ free(sp->slice);
+ free(sp->name);
+ free(sp);
+ return (ENOMEM);
+ }
+
+ sp->next = svm_listp;
+ svm_listp = sp;
+
+ return (0);
+}
+
+static int
+diskset_info(mdsetname_t *sp)
+{
+ md_error_t error = *mdl_mdnullerror;
+ md_replicalist_t *replica_list = NULL;
+ mdnamelist_t *trans_list = NULL;
+ mdnamelist_t *raid_list = NULL;
+ mdnamelist_t *stripe_list = NULL;
+ mdnamelist_t *sp_list = NULL;
+ mdnamelist_t *spare_list = NULL;
+
+ if ((mdl_metareplicalist)(sp, MD_BASICNAME_OK, &replica_list, &error)
+ >= 0) {
+ md_replicalist_t *nlp;
+
+ for (nlp = replica_list; nlp != NULL; nlp = nlp->rl_next) {
+ if (new_entry(nlp->rl_repp->r_namep->bname, "mdb",
+ nlp->rl_repp->r_namep->cname, sp)) {
+ (mdl_metafreereplicalist)(replica_list);
+ return (ENOMEM);
+ }
+ }
+ (mdl_metafreereplicalist)(replica_list);
+
+ } else {
+ (mdl_mdclrerror)(&error);
+ /* there are no metadb's; that is ok, no need to check the rest */
+ return (0);
+ }
+ (mdl_mdclrerror)(&error);
+
+ if ((mdl_meta_get_trans_names)(sp, &trans_list, 0, &error) >= 0) {
+ mdnamelist_t *nlp;
+
+ for (nlp = trans_list; nlp != NULL; nlp = nlp->next) {
+ if (new_entry(nlp->namep->bname, "trans", nlp->namep->cname,
+ sp)) {
+ free_names(trans_list);
+ return (ENOMEM);
+ }
+ }
+
+ free_names(trans_list);
+ }
+ (mdl_mdclrerror)(&error);
+
+ if ((mdl_meta_get_raid_names)(sp, &raid_list, 0, &error) >= 0) {
+ mdnamelist_t *nlp;
+
+ for (nlp = raid_list; nlp != NULL; nlp = nlp->next) {
+ mdname_t *mdn;
+ md_raid_t *raid;
+
+ mdn = (mdl_metaname)(&sp, nlp->namep->cname, &error);
+ (mdl_mdclrerror)(&error);
+ if (mdn == NULL) {
+ continue;
+ }
+
+ raid = (mdl_meta_get_raid)(sp, mdn, &error);
+ (mdl_mdclrerror)(&error);
+
+ if (raid != NULL) {
+ int i;
+
+ for (i = 0; i < raid->cols.cols_len; i++) {
+ if (new_entry(raid->cols.cols_val[i].colnamep->bname,
+ "raid", nlp->namep->cname, sp)) {
+ free_names(raid_list);
+ return (ENOMEM);
+ }
+ }
+ }
+ }
+
+ free_names(raid_list);
+ }
+ (mdl_mdclrerror)(&error);
+
+ if ((mdl_meta_get_stripe_names)(sp, &stripe_list, 0, &error) >= 0) {
+ mdnamelist_t *nlp;
+
+ for (nlp = stripe_list; nlp != NULL; nlp = nlp->next) {
+ mdname_t *mdn;
+ md_stripe_t *stripe;
+
+ mdn = (mdl_metaname)(&sp, nlp->namep->cname, &error);
+ (mdl_mdclrerror)(&error);
+ if (mdn == NULL) {
+ continue;
+ }
+
+ stripe = (mdl_meta_get_stripe)(sp, mdn, &error);
+ (mdl_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(component->compnamep->bname, "stripe",
+ nlp->namep->cname, sp)) {
+ free_names(stripe_list);
+ return (ENOMEM);
+ }
+ }
+ }
+ }
+ }
+
+ free_names(stripe_list);
+ }
+ (mdl_mdclrerror)(&error);
+
+ if ((mdl_meta_get_sp_names)(sp, &sp_list, 0, &error) >= 0) {
+ mdnamelist_t *nlp;
+
+ for (nlp = sp_list; nlp != NULL; nlp = nlp->next) {
+ mdname_t *mdn;
+ md_sp_t *soft_part;
+
+ mdn = (mdl_metaname)(&sp, nlp->namep->cname, &error);
+ (mdl_mdclrerror)(&error);
+ if (mdn == NULL) {
+ continue;
+ }
+
+ soft_part = (mdl_meta_get_sp)(sp, mdn, &error);
+ (mdl_mdclrerror)(&error);
+
+ if (soft_part != NULL) {
+ if (new_entry(soft_part->compnamep->bname, "sp",
+ nlp->namep->cname, sp)) {
+ free_names(sp_list);
+ return (ENOMEM);
+ }
+ }
+ }
+
+ free_names(sp_list);
+ }
+ (mdl_mdclrerror)(&error);
+
+ if ((mdl_meta_get_hotspare_names)(sp, &spare_list, 0, &error) >= 0) {
+ mdnamelist_t *nlp;
+
+ for (nlp = spare_list; nlp != NULL; nlp = nlp->next) {
+ if (new_entry(nlp->namep->bname, "hs", nlp->namep->cname, sp)) {
+ free_names(spare_list);
+ return (ENOMEM);
+ }
+ }
+
+ free_names(spare_list);
+ }
+
+ (mdl_mdclrerror)(&error);
+
+ return (0);
+}
+
+/*
+ * SVM uses "drive names" (ctd name without trailing slice) for drives
+ * in disksets. Since it massages these names there is no direct correspondence
+ * with the slice device names in /dev. So, we need to massage these names
+ * back to something we can match on when a slice comes in. We create an
+ * entry for each possible slice since we don't know what slices actually
+ * exist. Slice 0 & 7 are probably enough, but the user could have
+ * repartitioned the drive after they added it to the diskset and removed the
+ * mdb.
+ */
+static int
+drive_in_diskset(char *dpath, char *setname)
+{
+ int i;
+ char path[MAXPATHLEN];
+
+ (void) strlcpy(path, dpath, sizeof (path));
+ if (strncmp(path, "/dev/rdsk/", 10) == 0) {
+ /* change rdsk to dsk */
+ char *p;
+
+ /* start p pointing to r in rdsk */
+ for (p = path + 5; *p; p++) {
+ *p = *(p + 1);
+ }
+ } else if (strncmp(path, "/dev/did/rdsk/", 14) == 0) {
+ /* change rdsk to dsk */
+ char *p;
+
+ /* start p pointing to r in rdsk */
+ for (p = path + 9; *p; p++) {
+ *p = *(p + 1);
+ }
+ }
+
+ for (i = 0; i < 8; i++) {
+ char slice[MAXPATHLEN];
+
+ (void) snprintf(slice, sizeof (slice), "%ss%d", path, i);
+ if (add_use_record(slice, "diskset", setname)) {
+ return (ENOMEM);
+ }
+ }
+
+ return (0);
+}
+
+static void
+event_handler()
+{
+ (void) rw_wrlock(&svm_lock);
+ free_svm(svm_listp);
+ svm_listp = NULL;
+ (mdl_metaflushnames)(0);
+ (void) load_svm();
+ (void) rw_unlock(&svm_lock);
+}
+
+static void
+free_names(mdnamelist_t *nlp)
+{
+ mdnamelist_t *p;
+
+ for (p = nlp; p != NULL; p = p->next) {
+ (mdl_meta_invalidate_name)(p->namep);
+ p->namep = NULL;
+ }
+ (mdl_metafreenamelist)(nlp);
+}
+
+/*
+ * Free the list of SVM entries.
+ */
+static void
+free_svm(struct svm_list *listp) {
+
+ struct svm_list *nextp;
+
+ while (listp != NULL) {
+ nextp = listp->next;
+ free((void *)listp->slice);
+ free((void *)listp->name);
+ free((void *)listp->type);
+ free((void *)listp);
+ listp = nextp;
+ }
+}
+
+/*
+ * Try to dynamically link the libmeta functions we need.
+ */
+static int
+init_svm()
+{
+ void *lh;
+
+ if ((lh = dlopen("/usr/lib/libmeta.so", RTLD_NOW)) == NULL) {
+ return (0);
+ }
+
+ mdl_get_max_sets = (set_t (*)(md_error_t *))dlsym(lh, "get_max_sets");
+
+ mdl_mdclrerror = (void(*)(md_error_t *))dlsym(lh, "mdclrerror");
+
+ mdl_mdnullerror = (md_error_t *)dlsym(lh, "mdnullerror");
+
+ mdl_metaflushnames = (void (*)(int))dlsym(lh, "metaflushnames");
+
+ mdl_metaflushsetname = (void (*)(mdsetname_t *))dlsym(lh,
+ "metaflushsetname");
+
+ mdl_metafreenamelist = (void (*)(mdnamelist_t *))dlsym(lh,
+ "metafreenamelist");
+
+ mdl_metafreereplicalist = (void (*)(md_replicalist_t *))dlsym(lh,
+ "metafreereplicalist");
+
+ mdl_metaget_drivedesc = (md_drive_desc *(*)(mdsetname_t *, int,
+ md_error_t *))dlsym(lh, "metaget_drivedesc");
+
+ mdl_metaname = (mdname_t *(*)(mdsetname_t **, char *, md_error_t *))
+ dlsym(lh, "metaname");
+
+ mdl_metareplicalist = (int (*)(mdsetname_t *, int, md_replicalist_t **,
+ md_error_t *))dlsym(lh, "metareplicalist");
+
+ mdl_metasetnosetname = (mdsetname_t *(*)(set_t, md_error_t *))dlsym(lh,
+ "metasetnosetname");
+
+ mdl_meta_get_hotspare_names = (int (*)(mdsetname_t *, mdnamelist_t **,
+ int, md_error_t *))dlsym(lh, "meta_get_hotspare_names");
+
+ mdl_meta_get_raid = (md_raid_t *(*)(mdsetname_t *, mdname_t *,
+ md_error_t *))dlsym(lh, "meta_get_raid");
+
+ mdl_meta_get_raid_names = (int (*)(mdsetname_t *, mdnamelist_t **,
+ int, md_error_t *))dlsym(lh, "meta_get_raid_names");
+
+ mdl_meta_get_sp = (md_sp_t *(*)(mdsetname_t *, mdname_t *,
+ md_error_t *))dlsym(lh, "meta_get_sp");
+
+ mdl_meta_get_sp_names = (int (*)(mdsetname_t *, mdnamelist_t **,
+ int, md_error_t *))dlsym(lh, "meta_get_sp_names");
+
+ mdl_meta_get_stripe = (md_stripe_t *(*)(mdsetname_t *, mdname_t *,
+ md_error_t *))dlsym(lh, "meta_get_stripe");
+
+ mdl_meta_get_stripe_names = (int (*)(mdsetname_t *, mdnamelist_t **,
+ int, md_error_t *))dlsym(lh, "meta_get_stripe_names");
+
+ mdl_meta_get_trans_names = (int (*)(mdsetname_t *, mdnamelist_t **,
+ int, md_error_t *))dlsym(lh, "meta_get_trans_names");
+
+ mdl_meta_invalidate_name = (void (*)(mdname_t *))dlsym(lh,
+ "meta_invalidate_name");
+
+ mdl_sdssc_bind_library = (void (*)())dlsym(lh, "sdssc_bind_library");
+
+ return (1);
+}
+
+/*
+ * Create a list of SVM devices
+ */
+static int
+load_svm()
+{
+ int max_sets;
+ md_error_t error = *mdl_mdnullerror;
+ int i;
+
+ if ((max_sets = (mdl_get_max_sets)(&error)) == 0) {
+ return (0);
+ }
+
+ if (!mdisok(&error)) {
+ (mdl_mdclrerror)(&error);
+ return (0);
+ }
+
+ /* for each possible set number, see if we really have a diskset */
+ for (i = 0; i < max_sets; i++) {
+ mdsetname_t *sp;
+
+ if ((sp = (mdl_metasetnosetname)(i, &error)) == NULL) {
+
+ if (!mdisok(&error) &&
+ mdisrpcerror(&error, RPC_PROGNOTREGISTERED)) {
+ /* metad rpc program not registered - no metasets */
+ break;
+ }
+
+ (mdl_mdclrerror)(&error);
+ continue;
+ }
+ (mdl_mdclrerror)(&error);
+
+ /* pick up drives in disksets with no mdbs/metadevices */
+ if (sp->setno != 0) {
+ md_drive_desc *dd;
+
+ dd = (mdl_metaget_drivedesc)(sp, MD_BASICNAME_OK | PRINT_FAST,
+ &error);
+ (mdl_mdclrerror)(&error);
+ for (; dd != NULL; dd = dd->dd_next) {
+ if (drive_in_diskset(dd->dd_dnp->rname, sp->setname)) {
+ (mdl_metaflushsetname)(sp);
+ return (ENOMEM);
+ }
+ }
+ }
+
+ if (diskset_info(sp)) {
+ (mdl_metaflushsetname)(sp);
+ return (ENOMEM);
+ }
+
+ (mdl_metaflushsetname)(sp);
+ }
+
+ (mdl_mdclrerror)(&error);
+
+ return (0);
+}
+
+static int
+new_entry(char *sname, char *type, char *mname, mdsetname_t *sp)
+{
+ mdname_t *mdn;
+ md_error_t error = *mdl_mdnullerror;
+
+ mdn = (mdl_metaname)(&sp, sname, &error);
+ if (!mdisok(&error)) {
+ (mdl_mdclrerror)(&error);
+ return (0);
+ }
+
+ if (mdn != NULL && (
+ mdn->drivenamep->type == MDT_ACCES ||
+ mdn->drivenamep->type == MDT_COMP ||
+ mdn->drivenamep->type == MDT_FAST_COMP)) {
+
+ return (add_use_record(mdn->bname, type, mname));
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/libdiskmgt/common/inuse_vxvm.c b/usr/src/lib/libdiskmgt/common/inuse_vxvm.c
new file mode 100644
index 0000000000..8b464dc70b
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/inuse_vxvm.c
@@ -0,0 +1,393 @@
+/*
+ * 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"
+
+/*
+ * Attempt to dynamically link in the Veritas libvxvmsc.so so that we can
+ * see if there are any Veritas volumes on any of the slices.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <strings.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <thread.h>
+#include <synch.h>
+#include <dlfcn.h>
+#include <link.h>
+#include <ctype.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+
+#define VXVM_NAME_SIZE 1
+#define VXVM_PATH_SIZE 2
+
+typedef char *vm_name_t;
+typedef char *vm_path_t;
+
+/*
+ * Pointers to libvxvmsc.so functions that we dynamically resolve.
+ */
+static int (*vxdl_libvxvm_get_version)(int version);
+static int (*vxdl_libvxvm_get_conf)(int param);
+static int (*vxdl_libvxvm_get_dgs)(int len, vm_name_t namep[]);
+static int (*vxdl_libvxvm_get_disks)(vm_name_t dgname, int len,
+ vm_path_t pathp[]);
+
+#define MAX_DISK_GROUPS 128
+#define MAX_DISKS_DG 1024
+
+struct vxvm_list {
+ struct vxvm_list *next;
+ char *slice;
+};
+
+static struct vxvm_list *vxvm_listp = NULL;
+static time_t timestamp = 0;
+static mutex_t vxvm_lock = DEFAULTMUTEX;
+
+static int add_use_record(char *devname);
+static void free_vxvm();
+static void *init_vxvm();
+static int is_ctds(char *name);
+static int load_vxvm();
+
+int
+inuse_vxvm(char *slice, nvlist_t *attrs, int *errp)
+{
+ int found = 0;
+ time_t curr_time;
+ char *sp = NULL;
+
+ *errp = 0;
+ if (slice == NULL) {
+ return (found);
+ }
+
+ /*
+ * Since vxvm "encapsulates" the disk we need to match on any
+ * slice passed in. Strip the slice component from the devname.
+ */
+ if (is_ctds(slice)) {
+ if ((sp = strrchr(slice, '/')) == NULL)
+ sp = slice;
+
+ while (*sp && *sp != 's')
+ sp++;
+
+ if (*sp)
+ *sp = 0;
+ else
+ sp = NULL;
+ }
+
+ (void) mutex_lock(&vxvm_lock);
+
+ curr_time = time(NULL);
+ if (timestamp < curr_time && (curr_time - timestamp) > 60) {
+ free_vxvm(); /* free old entries */
+ *errp = load_vxvm(); /* load the cache */
+
+ timestamp = curr_time;
+ }
+
+ if (*errp == 0) {
+ struct vxvm_list *listp;
+
+ listp = vxvm_listp;
+ while (listp != NULL) {
+ if (strcmp(slice, listp->slice) == 0) {
+ libdiskmgt_add_str(attrs, DM_USED_BY, DM_USE_VXVM, errp);
+ libdiskmgt_add_str(attrs, DM_USED_NAME, "", errp);
+ found = 1;
+ break;
+ }
+ listp = listp->next;
+ }
+ }
+
+ (void) mutex_unlock(&vxvm_lock);
+
+ /* restore slice name to orignal value */
+ if (sp != NULL)
+ *sp = 's';
+
+ return (found);
+}
+
+static int
+add_use_record(char *devname)
+{
+ struct vxvm_list *sp;
+
+ sp = (struct vxvm_list *)malloc(sizeof (struct vxvm_list));
+ if (sp == NULL) {
+ return (ENOMEM);
+ }
+
+ if ((sp->slice = strdup(devname)) == NULL) {
+ free(sp);
+ return (ENOMEM);
+ }
+
+ sp->next = vxvm_listp;
+ vxvm_listp = sp;
+
+ /*
+ * Since vxvm "encapsulates" the disk we need to match on any
+ * slice passed in. Strip the slice component from the devname.
+ */
+ if (is_ctds(sp->slice)) {
+ char *dp;
+
+ if ((dp = strrchr(sp->slice, '/')) == NULL)
+ dp = sp->slice;
+
+ while (*dp && *dp != 's')
+ dp++;
+ *dp = 0;
+ }
+
+ return (0);
+}
+
+/*
+ * If the input name is in c[t]ds format then return 1, otherwise return 0.
+ */
+static int
+is_ctds(char *name)
+{
+ char *p;
+
+ if ((p = strrchr(name, '/')) == NULL)
+ p = name;
+ else
+ p++;
+
+ if (*p++ != 'c') {
+ return (0);
+ }
+ /* skip controller digits */
+ while (isdigit(*p)) {
+ p++;
+ }
+
+ /* handle optional target */
+ if (*p == 't') {
+ p++;
+ /* skip over target */
+ while (isdigit(*p) || isupper(*p)) {
+ p++;
+ }
+ }
+
+ if (*p++ != 'd') {
+ return (0);
+ }
+ while (isdigit(*p)) {
+ p++;
+ }
+
+ if (*p++ != 's') {
+ return (0);
+ }
+
+ /* check the slice number */
+ while (isdigit(*p)) {
+ p++;
+ }
+
+ if (*p != 0) {
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * Free the list of vxvm entries.
+ */
+static void
+free_vxvm()
+{
+ struct vxvm_list *listp = vxvm_listp;
+ struct vxvm_list *nextp;
+
+ while (listp != NULL) {
+ nextp = listp->next;
+ free((void *)listp->slice);
+ free((void *)listp);
+ listp = nextp;
+ }
+
+ vxvm_listp = NULL;
+}
+
+/*
+ * Try to dynamically link the vxvm functions we need.
+ */
+static void *
+init_vxvm()
+{
+ void *lh;
+
+ if ((lh = dlopen("libvxvmsc.so", RTLD_NOW)) == NULL) {
+ return (lh);
+ }
+
+ if ((vxdl_libvxvm_get_version = (int (*)(int))dlsym(lh,
+ "libvxvm_get_version")) == NULL) {
+ (void) dlclose(lh);
+ return (NULL);
+ }
+
+ if ((vxdl_libvxvm_get_conf = (int (*)(int))dlsym(lh,
+ "libvxvm_get_conf")) == NULL) {
+ (void) dlclose(lh);
+ return (NULL);
+ }
+
+ if ((vxdl_libvxvm_get_dgs = (int (*)(int, vm_name_t []))dlsym(lh,
+ "libvxvm_get_dgs")) == NULL) {
+ (void) dlclose(lh);
+ return (NULL);
+ }
+
+ if ((vxdl_libvxvm_get_disks = (int (*)(vm_name_t, int, vm_path_t []))
+ dlsym(lh, "libvxvm_get_disks")) == NULL) {
+ (void) dlclose(lh);
+ return (NULL);
+ }
+
+ return (lh);
+}
+
+static int
+load_vxvm()
+{
+ void *lh;
+ int vers;
+ int nsize;
+ int psize;
+ int n_disk_groups;
+ vm_name_t *namep;
+ char *pnp;
+ vm_path_t *pathp;
+ int i;
+
+ if ((lh = init_vxvm()) == NULL) {
+ /* No library. */
+ return (0);
+ }
+
+ vers = (vxdl_libvxvm_get_version)(1 << 8);
+ if (vers == -1) {
+ /* unsupported version */
+ (void) dlclose(lh);
+ return (0);
+ }
+
+ nsize = (vxdl_libvxvm_get_conf)(VXVM_NAME_SIZE);
+ psize = (vxdl_libvxvm_get_conf)(VXVM_PATH_SIZE);
+
+ if (nsize == -1 || psize == -1) {
+ (void) dlclose(lh);
+ return (0);
+ }
+
+ namep = (vm_name_t *)calloc(MAX_DISK_GROUPS, nsize);
+ if (namep == NULL) {
+ (void) dlclose(lh);
+ return (ENOMEM);
+ }
+
+ pathp = (vm_path_t *)calloc(MAX_DISKS_DG, psize);
+ if (pathp == NULL) {
+ (void) dlclose(lh);
+ free(namep);
+ return (ENOMEM);
+ }
+
+ n_disk_groups = (vxdl_libvxvm_get_dgs)(MAX_DISK_GROUPS, namep);
+ if (n_disk_groups < 0) {
+ (void) dlclose(lh);
+ free(namep);
+ free(pathp);
+ return (0);
+ }
+
+ pnp = (char *)namep;
+ for (i = 0; i < n_disk_groups; i++) {
+ int n_disks;
+
+ n_disks = (vxdl_libvxvm_get_disks)(pnp, MAX_DISKS_DG, pathp);
+
+ if (n_disks >= 0) {
+ int j;
+ char *ppp;
+
+ ppp = (char *)pathp;
+ for (j = 0; j < n_disks; j++) {
+
+ if (strncmp(ppp, "/dev/vx/", 8) == 0) {
+ char *pslash;
+ char nm[MAXPATHLEN];
+
+ pslash = strrchr(ppp, '/');
+ pslash++;
+
+ (void) snprintf(nm, sizeof (nm), "/dev/dsk/%s", pslash);
+ if (add_use_record(nm)) {
+ (void) dlclose(lh);
+ free(pathp);
+ free(namep);
+ return (ENOMEM);
+ }
+ } else {
+ if (add_use_record(ppp)) {
+ (void) dlclose(lh);
+ free(pathp);
+ free(namep);
+ return (ENOMEM);
+ }
+ }
+
+ ppp += psize;
+ }
+ }
+
+ pnp += nsize;
+ }
+
+ (void) dlclose(lh);
+ free(pathp);
+ free(namep);
+
+ return (0);
+}
diff --git a/usr/src/lib/libdiskmgt/common/libdiskmgt.h b/usr/src/lib/libdiskmgt/common/libdiskmgt.h
new file mode 100644
index 0000000000..d9c2909539
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/libdiskmgt.h
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBDISKMGT_H
+#define _LIBDISKMGT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libnvpair.h>
+
+/* typedef void *dm_descriptor_t; */
+typedef uint64_t dm_descriptor_t;
+
+typedef enum {
+ DM_DRIVE = 0,
+ DM_CONTROLLER,
+ DM_MEDIA,
+ DM_SLICE,
+ DM_PARTITION,
+ DM_PATH,
+ DM_ALIAS,
+ DM_BUS
+} dm_desc_type_t;
+
+
+typedef enum {
+ DM_DT_UNKNOWN = 0,
+ DM_DT_FIXED,
+ DM_DT_ZIP,
+ DM_DT_JAZ,
+ DM_DT_FLOPPY,
+ DM_DT_MO_ERASABLE,
+ DM_DT_MO_WRITEONCE,
+ DM_DT_AS_MO,
+ DM_DT_CDROM,
+ DM_DT_CDR,
+ DM_DT_CDRW,
+ DM_DT_DVDROM,
+ DM_DT_DVDR,
+ DM_DT_DVDRAM,
+ DM_DT_DVDRW,
+ DM_DT_DDCDROM,
+ DM_DT_DDCDR,
+ DM_DT_DDCDRW
+} dm_drive_type_t;
+
+typedef enum {
+ DM_MT_UNKNOWN = 0,
+ DM_MT_FIXED,
+ DM_MT_FLOPPY,
+ DM_MT_CDROM,
+ DM_MT_ZIP,
+ DM_MT_JAZ,
+ DM_MT_CDR,
+ DM_MT_CDRW,
+ DM_MT_DVDROM,
+ DM_MT_DVDR,
+ DM_MT_DVDRAM,
+ DM_MT_MO_ERASABLE,
+ DM_MT_MO_WRITEONCE,
+ DM_MT_AS_MO
+} dm_media_type_t;
+
+#define DM_FILTER_END -1
+
+/* drive stat name */
+typedef enum {
+ DM_DRV_STAT_PERFORMANCE = 0,
+ DM_DRV_STAT_DIAGNOSTIC,
+ DM_DRV_STAT_TEMPERATURE
+} dm_drive_stat_t;
+
+/* slice stat name */
+typedef enum {
+ DM_SLICE_STAT_USE = 0
+} dm_slice_stat_t;
+
+/* attribute definitions */
+
+/* drive */
+#define DM_DISK_UP 1
+#define DM_DISK_DOWN 0
+
+#define DM_CLUSTERED "clustered"
+#define DM_DRVTYPE "drvtype"
+#define DM_FAILING "failing"
+#define DM_LOADED "loaded" /* also in media */
+#define DM_NDNRERRS "ndevice_not_ready_errors"
+#define DM_NBYTESREAD "nbytes_read"
+#define DM_NBYTESWRITTEN "nbytes_written"
+#define DM_NHARDERRS "nhard_errors"
+#define DM_NILLREQERRS "nillegal_req_errors"
+#define DM_NMEDIAERRS "nmedia_errors"
+#define DM_NNODEVERRS "nno_dev_errors"
+#define DM_NREADOPS "nread_ops"
+#define DM_NRECOVERRS "nrecoverable_errors"
+#define DM_NSOFTERRS "nsoft_errors"
+#define DM_NTRANSERRS "ntransport_errors"
+#define DM_NWRITEOPS "nwrite_ops"
+#define DM_OPATH "opath"
+#define DM_PRODUCT_ID "product_id"
+#define DM_REMOVABLE "removable" /* also in media */
+#define DM_RPM "rpm"
+#define DM_STATUS "status"
+#define DM_SYNC_SPEED "sync_speed"
+#define DM_TEMPERATURE "temperature"
+#define DM_VENDOR_ID "vendor_id"
+#define DM_WIDE "wide" /* also on controller */
+#define DM_WWN "wwn"
+
+/* bus */
+#define DM_BTYPE "btype"
+#define DM_CLOCK "clock" /* also on controller */
+#define DM_PNAME "pname"
+
+/* controller */
+#define DM_FAST "fast"
+#define DM_FAST20 "fast20"
+#define DM_FAST40 "fast40"
+#define DM_FAST80 "fast80"
+#define DM_MULTIPLEX "multiplex"
+#define DM_PATH_STATE "path_state"
+
+#define DM_CTYPE_ATA "ata"
+#define DM_CTYPE_SCSI "scsi"
+#define DM_CTYPE_FIBRE "fibre channel"
+#define DM_CTYPE_USB "usb"
+#define DM_CTYPE_UNKNOWN "unknown"
+
+/* media */
+#define DM_BLOCKSIZE "blocksize"
+#define DM_FDISK "fdisk"
+#define DM_MTYPE "mtype"
+#define DM_NACTUALCYLINDERS "nactual_cylinders"
+#define DM_NALTCYLINDERS "nalt_cylinders"
+#define DM_NCYLINDERS "ncylinders"
+#define DM_NHEADS "nheads"
+#define DM_NPHYSCYLINDERS "nphys_cylinders"
+#define DM_NSECTORS "nsectors" /* also in partition */
+#define DM_SIZE "size" /* also in slice */
+#define DM_NACCESSIBLE "naccessible"
+#define DM_LABEL "label"
+
+/* partition */
+#define DM_BCYL "bcyl"
+#define DM_BHEAD "bhead"
+#define DM_BOOTID "bootid"
+#define DM_BSECT "bsect"
+#define DM_ECYL "ecyl"
+#define DM_EHEAD "ehead"
+#define DM_ESECT "esect"
+#define DM_PTYPE "ptype"
+#define DM_RELSECT "relsect"
+
+/* slice */
+#define DM_DEVICEID "deviceid"
+#define DM_DEVT "devt"
+#define DM_INDEX "index"
+#define DM_EFI_NAME "name"
+#define DM_MOUNTPOINT "mountpoint"
+#define DM_LOCALNAME "localname"
+#define DM_START "start"
+#define DM_TAG "tag"
+#define DM_FLAG "flag"
+#define DM_EFI "efi" /* also on media */
+#define DM_USED_BY "used_by"
+#define DM_USED_NAME "used_name"
+#define DM_USE_MOUNT "mount"
+#define DM_USE_SVM "svm"
+#define DM_USE_LU "lu"
+#define DM_USE_DUMP "dump"
+#define DM_USE_VXVM "vxvm"
+#define DM_USE_FS "fs"
+#define DM_USE_VFSTAB "vfstab"
+
+/* event */
+#define DM_EV_NAME "name"
+#define DM_EV_DTYPE "edtype"
+#define DM_EV_TYPE "evtype"
+#define DM_EV_TADD "add"
+#define DM_EV_TREMOVE "remove"
+#define DM_EV_TCHANGE "change"
+
+/* findisks */
+#define DM_CTYPE "ctype"
+#define DM_LUN "lun"
+#define DM_TARGET "target"
+
+void dm_free_descriptors(dm_descriptor_t *desc_list);
+void dm_free_descriptor(dm_descriptor_t desc);
+void dm_free_name(char *name);
+
+dm_descriptor_t *dm_get_descriptors(dm_desc_type_t type, int filter[],
+ int *errp);
+dm_descriptor_t *dm_get_associated_descriptors(dm_descriptor_t desc,
+ dm_desc_type_t type, int *errp);
+dm_desc_type_t *dm_get_associated_types(dm_desc_type_t type);
+dm_descriptor_t dm_get_descriptor_by_name(dm_desc_type_t desc_type,
+ char *name, int *errp);
+char *dm_get_name(dm_descriptor_t desc, int *errp);
+dm_desc_type_t dm_get_type(dm_descriptor_t desc);
+nvlist_t *dm_get_attributes(dm_descriptor_t desc, int *errp);
+nvlist_t *dm_get_stats(dm_descriptor_t desc, int stat_type,
+ int *errp);
+void dm_init_event_queue(void(*callback)(nvlist_t *, int),
+ int *errp);
+nvlist_t *dm_get_event(int *errp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBDISKMGT_H */
diff --git a/usr/src/lib/libdiskmgt/common/llib-ldiskmgt b/usr/src/lib/libdiskmgt/common/llib-ldiskmgt
new file mode 100644
index 0000000000..d4fd481b8f
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/llib-ldiskmgt
@@ -0,0 +1,33 @@
+/*
+ * 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 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
diff --git a/usr/src/lib/libdiskmgt/common/media.c b/usr/src/lib/libdiskmgt/common/media.c
new file mode 100644
index 0000000000..364ff92d2a
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/media.c
@@ -0,0 +1,695 @@
+/*
+ * 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 <fcntl.h>
+#include <libdevinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <stropts.h>
+#include <sys/dkio.h>
+#include <sys/sunddi.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/vtoc.h>
+#include <volmgt.h>
+#include <sys/efi_partition.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+#include "partition.h"
+
+#define IOCTLRETRIES 2
+#define IOCTLRETRYINTERVAL 1
+
+static descriptor_t **apply_filter(descriptor_t **media, int filter[],
+ int *errp);
+static int get_attrs(disk_t *dp, int fd, nvlist_t *attrs);
+static int get_non_volm_name(disk_t *dp, char *mname, int size);
+static int get_media_type(uint_t media_type);
+static int desc_ok(descriptor_t *dp);
+
+/*
+ * This function gets the descriptors we are associated with.
+ */
+descriptor_t **
+media_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type,
+ int *errp)
+{
+ if (!desc_ok(desc)) {
+ *errp = ENODEV;
+ return (NULL);
+ }
+
+ switch (type) {
+ case DM_DRIVE:
+ return (drive_get_assocs(desc, errp));
+ case DM_PARTITION:
+ return (partition_get_assocs(desc, errp));
+ case DM_SLICE:
+ return (slice_get_assocs(desc, errp));
+ }
+
+ *errp = EINVAL;
+ return (NULL);
+}
+
+/*
+ * Get the media descriptors for the given drive/partition/slice.
+ */
+descriptor_t **
+media_get_assocs(descriptor_t *dp, int *errp)
+{
+ descriptor_t **media;
+ char mname[MAXPATHLEN];
+
+ if (!media_read_name(dp->p.disk, mname, sizeof (mname))) {
+ /* For drives, this means no media but slice/part. require media. */
+ if (dp->type == DM_DRIVE) {
+ return (libdiskmgt_empty_desc_array(errp));
+ } else {
+ *errp = ENODEV;
+ return (NULL);
+ }
+ }
+
+ /* make the snapshot */
+ media = (descriptor_t **)calloc(2, sizeof (descriptor_t *));
+ if (media == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ media[0] = cache_get_desc(DM_MEDIA, dp->p.disk, mname, NULL, errp);
+ if (*errp != 0) {
+ free(media);
+ return (NULL);
+ }
+ media[1] = NULL;
+
+ *errp = 0;
+ return (media);
+}
+
+nvlist_t *
+media_get_attributes(descriptor_t *dp, int *errp)
+{
+ nvlist_t *attrs = NULL;
+ int fd;
+
+ if (!desc_ok(dp)) {
+ *errp = ENODEV;
+ return (NULL);
+ }
+
+ if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ fd = drive_open_disk(dp->p.disk, NULL, 0);
+
+ if ((*errp = get_attrs(dp->p.disk, fd, attrs)) != 0) {
+ nvlist_free(attrs);
+ attrs = NULL;
+ }
+
+ if (fd >= 0) {
+ (void) close(fd);
+ }
+
+ return (attrs);
+}
+
+descriptor_t *
+media_get_descriptor_by_name(char *name, int *errp)
+{
+ descriptor_t **media;
+ int i;
+ descriptor_t *medium = NULL;
+
+ media = cache_get_descriptors(DM_MEDIA, errp);
+ if (*errp != 0) {
+ return (NULL);
+ }
+
+ for (i = 0; media[i]; i++) {
+ if (libdiskmgt_str_eq(name, media[i]->name)) {
+ medium = media[i];
+ } else {
+ /* clean up the unused descriptors */
+ cache_free_descriptor(media[i]);
+ }
+ }
+ free(media);
+
+ if (medium == NULL) {
+ *errp = ENODEV;
+ }
+
+ return (medium);
+}
+
+descriptor_t **
+media_get_descriptors(int filter[], int *errp)
+{
+ descriptor_t **media;
+
+ media = cache_get_descriptors(DM_MEDIA, errp);
+ if (*errp != 0) {
+ return (NULL);
+ }
+
+ if (filter != NULL && filter[0] != DM_FILTER_END) {
+ descriptor_t **found;
+
+ found = apply_filter(media, filter, errp);
+ if (*errp != 0) {
+ media = NULL;
+ } else {
+ media = found;
+ }
+ }
+
+ return (media);
+}
+
+char *
+media_get_name(descriptor_t *desc)
+{
+ return (desc->name);
+}
+
+/* ARGSUSED */
+nvlist_t *
+media_get_stats(descriptor_t *dp, int stat_type, int *errp)
+{
+ /* There are no stat types defined for media */
+ *errp = EINVAL;
+ return (NULL);
+}
+
+/*
+ * Get the removable media volume manager devpath for the disk. This is the
+ * name we need to open that will work with vold.
+ * Return 1 if under volm control, 0 if not under volm control.
+ * The string in mediapath will be empty if the drive is under volm control
+ * but there is no media loaded.
+ */
+int
+media_get_volm_path(disk_t *diskp, char *mediapath, int size)
+{
+ char vname[MAXPATHLEN];
+ char *volname;
+ char *media_name;
+
+ if (!diskp->removable || !volmgt_running()) {
+ return (0);
+ }
+
+ /*
+ * The volume manager is running, so we have to check if this removable
+ * drive is under volm control or not.
+ */
+
+ /*
+ * We must check if this drive is under volume management control and
+ * what devpath to use.
+ * Note that we have to do this every time for drives that are not
+ * under the control of the volume manager, since the volume manager
+ * might have taken control since the last time we checked.
+ */
+ if (diskp->volm_path_set == 0) {
+ alias_t *ap;
+ slice_t *dp;
+
+ if ((ap = diskp->aliases) == NULL) {
+ return (0);
+ }
+
+ /* Check each devpath to see if it is under volm control. */
+ dp = ap->devpaths;
+ while (dp != NULL) {
+ slice_rdsk2dsk(dp->devpath, vname, sizeof (vname));
+ if (volmgt_inuse(vname)) {
+ break;
+ }
+
+ dp = dp->next;
+ }
+
+ if (dp != NULL) {
+ /* Volume manager is managing the devpath that dp points to. */
+ diskp->volm_path = dp->devpath;
+ diskp->volm_path_set = 1;
+ }
+ }
+
+ if (diskp->volm_path_set == 0) {
+ /* The volume manager is not managing any of the devpaths. */
+ return (0);
+ }
+
+ if (dm_debug > 1) {
+ (void) fprintf(stderr, "INFO: chk vol: %s\n", diskp->volm_path);
+ }
+
+ slice_rdsk2dsk(diskp->volm_path, vname, sizeof (vname));
+ volname = volmgt_symname(vname);
+ if (volname == NULL) {
+ mediapath[0] = 0;
+ return (1);
+ }
+
+ media_name = media_findname(volname);
+ free(volname);
+ if (media_name == NULL) {
+ mediapath[0] = 0;
+ return (1);
+ }
+
+ (void) strlcpy(mediapath, media_name, size);
+ free(media_name);
+ return (1);
+}
+
+int
+media_make_descriptors()
+{
+ int error;
+ disk_t *dp;
+ char mname[MAXPATHLEN];
+
+ dp = cache_get_disklist();
+ while (dp != NULL) {
+ if (media_read_name(dp, mname, sizeof (mname))) {
+ cache_load_desc(DM_MEDIA, dp, mname, NULL, &error);
+ if (error != 0) {
+ return (error);
+ }
+ }
+
+ dp = dp->next;
+ }
+
+ return (0);
+}
+
+/*
+ * Read the media information.
+ */
+int
+media_read_info(int fd, struct dk_minfo *minfo)
+{
+ int status;
+ int tries = 0;
+
+ minfo->dki_media_type = 0;
+
+ /*
+ * This ioctl can fail if the media is not loaded or spun up.
+ * Retrying can sometimes succeed since the first ioctl will have
+ * started the media before the ioctl timed out so the media may be
+ * spun up on the subsequent attempt.
+ */
+ while ((status = ioctl(fd, DKIOCGMEDIAINFO, minfo)) < 0) {
+ tries++;
+ if (tries >= IOCTLRETRIES) {
+ break;
+ }
+ (void) sleep(IOCTLRETRYINTERVAL);
+ }
+
+ if (status < 0) {
+ return (0);
+ }
+
+ return (1);
+}
+
+/* return 1 if there is media, 0 if not. */
+int
+media_read_name(disk_t *dp, char *mname, int size)
+{
+ int under_volm;
+ char rmmedia_devpath[MAXPATHLEN];
+
+ mname[0] = 0;
+
+ if (!dp->removable) {
+ /* not removable, so media name is devid */
+ if (dp->device_id != NULL) {
+ (void) strlcpy(mname, dp->device_id, size);
+ }
+ return (1);
+ }
+
+ /* This is a removable media drive. */
+
+ /* Get 1 if under volm control, 0 if not */
+ under_volm = media_get_volm_path(dp, rmmedia_devpath,
+ sizeof (rmmedia_devpath));
+
+ if (under_volm) {
+ /* under volm control */
+ if (rmmedia_devpath[0] == 0) {
+ /* no media */
+ return (0);
+ }
+ (void) strlcpy(mname, rmmedia_devpath, size);
+ return (1);
+
+ } else {
+ /* not under volm control */
+ return (get_non_volm_name(dp, mname, size));
+ }
+}
+
+static descriptor_t **
+apply_filter(descriptor_t **media, int filter[], int *errp)
+{
+ descriptor_t **found;
+ int i;
+ int cnt = 0;
+ int pos;
+
+ /* count the number of media in the snapshot */
+ for (i = 0; media[i]; i++) {
+ cnt++;
+ }
+
+ found = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
+ if (found == NULL) {
+ *errp = ENOMEM;
+ cache_free_descriptors(media);
+ return (NULL);
+ }
+
+ pos = 0;
+ for (i = 0; media[i]; i++) {
+ int fd;
+ struct dk_minfo minfo;
+
+ if ((fd = drive_open_disk(media[i]->p.disk, NULL, 0)) < 0) {
+ continue;
+ }
+
+ if (media_read_info(fd, &minfo)) {
+ int mtype;
+ int j;
+ int match;
+
+ mtype = get_media_type(minfo.dki_media_type);
+
+ match = 0;
+ for (j = 0; filter[j] != DM_FILTER_END; j++) {
+ if (mtype == filter[j]) {
+ found[pos++] = media[i];
+ match = 1;
+ break;
+ }
+ }
+
+ if (!match) {
+ cache_free_descriptor(media[i]);
+ }
+ }
+#ifdef i386
+ /* XXX Work around bug 4725434 */
+ else if (!media[i]->p.disk->removable) {
+ int j;
+ int match;
+
+ match = 0;
+ for (j = 0; filter[j] != DM_FILTER_END; j++) {
+ if (DM_MT_FIXED == filter[j]) {
+ found[pos++] = media[i];
+ match = 1;
+ break;
+ }
+ }
+
+ if (!match) {
+ cache_free_descriptor(media[i]);
+ }
+ }
+#endif
+
+ (void) close(fd);
+ }
+ found[pos] = NULL;
+ free(media);
+
+ *errp = 0;
+ return (found);
+}
+
+/* return 1 if the media descriptor is still valid, 0 if not. */
+static int
+desc_ok(descriptor_t *dp)
+{
+ /* First verify the media name for removable media */
+ if (dp->p.disk->removable) {
+ char mname[MAXPATHLEN];
+
+ if (!media_read_name(dp->p.disk, mname, sizeof (mname))) {
+ return (0);
+ }
+
+ if (mname[0] == 0) {
+ return (libdiskmgt_str_eq(dp->name, NULL));
+ } else {
+ return (libdiskmgt_str_eq(dp->name, mname));
+ }
+ }
+
+ return (1);
+}
+
+static int
+get_attrs(disk_t *dp, int fd, nvlist_t *attrs)
+{
+ struct dk_minfo minfo;
+ struct dk_geom geometry;
+
+ if (fd < 0) {
+ return (ENODEV);
+ }
+
+ bzero(&minfo, sizeof (struct dk_minfo));
+
+ /* The first thing to do is read the media */
+ if (!media_read_info(fd, &minfo)) {
+ /* XXX Work around bug 4725434 */
+#ifdef i386
+ if (dp->removable)
+#endif
+ return (ENODEV);
+ }
+
+ if (partition_has_fdisk(dp, fd)) {
+ if (nvlist_add_boolean(attrs, DM_FDISK) != 0) {
+ return (ENOMEM);
+ }
+ }
+
+ if (dp->removable) {
+ if (nvlist_add_boolean(attrs, DM_REMOVABLE) != 0) {
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_boolean(attrs, DM_LOADED) != 0) {
+ return (ENOMEM);
+ }
+ }
+
+ if (nvlist_add_uint64(attrs, DM_SIZE, minfo.dki_capacity) != 0) {
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_uint32(attrs, DM_BLOCKSIZE, minfo.dki_lbsize) != 0) {
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_uint32(attrs, DM_MTYPE,
+ get_media_type(minfo.dki_media_type)) != 0) {
+ return (ENOMEM);
+ }
+
+ /* only for disks < 1TB */
+ if (ioctl(fd, DKIOCGGEOM, &geometry) >= 0) {
+ struct vtoc vtoc;
+
+ if (nvlist_add_uint64(attrs, DM_START, 0) != 0) {
+ return (ENOMEM);
+ }
+ if (nvlist_add_uint64(attrs, DM_NACCESSIBLE,
+ geometry.dkg_ncyl * geometry.dkg_nhead * geometry.dkg_nsect)
+ != 0) {
+ return (ENOMEM);
+ }
+ if (nvlist_add_uint32(attrs, DM_NCYLINDERS, geometry.dkg_ncyl)
+ != 0) {
+ return (ENOMEM);
+ }
+ if (nvlist_add_uint32(attrs, DM_NPHYSCYLINDERS, geometry.dkg_pcyl)
+ != 0) {
+ return (ENOMEM);
+ }
+ if (nvlist_add_uint32(attrs, DM_NALTCYLINDERS, geometry.dkg_acyl)
+ != 0) {
+ return (ENOMEM);
+ }
+ if (nvlist_add_uint32(attrs, DM_NHEADS, geometry.dkg_nhead) != 0) {
+ return (ENOMEM);
+ }
+ if (nvlist_add_uint32(attrs, DM_NSECTORS, geometry.dkg_nsect)
+ != 0) {
+ return (ENOMEM);
+ }
+
+ if (read_vtoc(fd, &vtoc) >= 0 && vtoc.v_volume[0] != 0) {
+ char label[LEN_DKL_VVOL + 1];
+
+ (void) snprintf(label, sizeof (label), "%.*s", LEN_DKL_VVOL,
+ vtoc.v_volume);
+ if (nvlist_add_string(attrs, DM_LABEL, label) != 0) {
+ return (ENOMEM);
+ }
+ }
+
+ } else {
+ /* check for disks > 1TB for accessible size */
+ struct dk_gpt *efip;
+
+ if (efi_alloc_and_read(fd, &efip) >= 0) {
+ diskaddr_t p8size = 0;
+
+ if (nvlist_add_boolean(attrs, DM_EFI) != 0) {
+ return (ENOMEM);
+ }
+ if (nvlist_add_uint64(attrs, DM_START, efip->efi_first_u_lba)
+ != 0) {
+ return (ENOMEM);
+ }
+ /* partition 8 is reserved on EFI labels */
+ if (efip->efi_nparts >= 9) {
+ p8size = efip->efi_parts[8].p_size;
+ }
+ if (nvlist_add_uint64(attrs, DM_NACCESSIBLE,
+ (efip->efi_last_u_lba - p8size) - efip->efi_first_u_lba)
+ != 0) {
+ efi_free(efip);
+ return (ENOMEM);
+ }
+ efi_free(efip);
+ }
+ }
+
+ /* This ioctl seems to be mainly for intel-based drives. */
+ if (ioctl(fd, DKIOCG_PHYGEOM, &geometry) >= 0) {
+ if (nvlist_add_uint32(attrs, DM_NACTUALCYLINDERS, geometry.dkg_ncyl)
+ != 0) {
+ return (ENOMEM);
+ }
+ }
+
+ return (0);
+}
+
+static int
+get_media_type(uint_t media_type)
+{
+ switch (media_type) {
+ case DK_UNKNOWN:
+ return (DM_MT_UNKNOWN);
+ case DK_MO_ERASABLE:
+ return (DM_MT_MO_ERASABLE);
+ case DK_MO_WRITEONCE:
+ return (DM_MT_MO_WRITEONCE);
+ case DK_AS_MO:
+ return (DM_MT_AS_MO);
+ case DK_CDROM:
+ return (DM_MT_CDROM);
+ case DK_CDR:
+ return (DM_MT_CDR);
+ case DK_CDRW:
+ return (DM_MT_CDRW);
+ case DK_DVDROM:
+ return (DM_MT_DVDROM);
+ case DK_DVDR:
+ return (DM_MT_DVDR);
+ case DK_DVDRAM:
+ return (DM_MT_DVDRAM);
+ case DK_FIXED_DISK:
+ return (DM_MT_FIXED);
+ case DK_FLOPPY:
+ return (DM_MT_FLOPPY);
+ case DK_ZIP:
+ return (DM_MT_ZIP);
+ case DK_JAZ:
+ return (DM_MT_JAZ);
+ default:
+ return (DM_MT_UNKNOWN);
+ }
+}
+
+/*
+ * This function handles removable media not under volume management.
+ */
+static int
+get_non_volm_name(disk_t *dp, char *mname, int size)
+{
+ int loaded;
+ int fd;
+
+ loaded = 0;
+
+ if ((fd = drive_open_disk(dp, NULL, 0)) >= 0) {
+ struct dk_minfo minfo;
+
+ if ((loaded = media_read_info(fd, &minfo))) {
+ struct vtoc vtoc;
+
+ if (read_vtoc(fd, &vtoc) >= 0) {
+ if (vtoc.v_volume[0] != NULL) {
+ if (LEN_DKL_VVOL < size) {
+ (void) strlcpy(mname, vtoc.v_volume, LEN_DKL_VVOL);
+ } else {
+ (void) strlcpy(mname, vtoc.v_volume, size);
+ }
+ }
+ }
+ }
+
+ (void) close(fd);
+ }
+
+ return (loaded);
+}
diff --git a/usr/src/lib/libdiskmgt/common/partition.c b/usr/src/lib/libdiskmgt/common/partition.c
new file mode 100644
index 0000000000..92e3e9579a
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/partition.c
@@ -0,0 +1,751 @@
+/*
+ * 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 <fcntl.h>
+#include <libdevinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/sunddi.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/dkio.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+#include "partition.h"
+
+#ifdef sparc
+#define les(val) ((((val)&0xFF)<<8)|(((val)>>8)&0xFF))
+#define lel(val) (((unsigned)(les((val)&0x0000FFFF))<<16) | \
+ (les((unsigned)((val)&0xffff0000)>>16)))
+#else
+#define les(val) (val)
+#define lel(val) (val)
+#endif
+
+#define ISIZE FD_NUMPART * sizeof (struct ipart)
+
+static int desc_ok(descriptor_t *dp);
+static int get_attrs(descriptor_t *dp, struct ipart *iparts,
+ nvlist_t *attrs);
+static int get_parts(disk_t *disk, struct ipart *iparts, char *opath,
+ int opath_len);
+static int open_disk(disk_t *diskp, char *opath, int len);
+static int has_slices(descriptor_t *desc, int *errp);
+
+descriptor_t **
+partition_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type,
+ int *errp)
+{
+ if (!desc_ok(desc)) {
+ *errp = ENODEV;
+ return (NULL);
+ }
+
+ switch (type) {
+ case DM_MEDIA:
+ return (media_get_assocs(desc, errp));
+ case DM_SLICE:
+ if (!has_slices(desc, errp)) {
+ if (*errp != 0) {
+ return (NULL);
+ }
+ return (libdiskmgt_empty_desc_array(errp));
+ }
+ return (slice_get_assocs(desc, errp));
+ }
+
+ *errp = EINVAL;
+ return (NULL);
+}
+
+/*
+ * This is called by media/slice to get the associated partitions.
+ * For a media desc. we just get all the partitions, but for a slice desc.
+ * we just get the active solaris partition.
+ */
+descriptor_t **
+partition_get_assocs(descriptor_t *desc, int *errp)
+{
+ descriptor_t **partitions;
+ int pos;
+ int i;
+ struct ipart iparts[FD_NUMPART];
+ char pname[MAXPATHLEN];
+ int conv_flag = 0;
+
+ if (get_parts(desc->p.disk, iparts, pname, sizeof (pname)) != 0) {
+ return (libdiskmgt_empty_desc_array(errp));
+ }
+
+ /* allocate the array for the descriptors */
+ partitions = (descriptor_t **)calloc(FD_NUMPART + 1,
+ sizeof (descriptor_t *));
+ if (partitions == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+#ifdef i386
+ {
+ /* convert part. name (e.g. c0d0p0) */
+ int len;
+
+ len = strlen(pname);
+ if (len > 1 && *(pname + (len - 2)) == 'p') {
+ conv_flag = 1;
+ *(pname + (len - 1)) = 0;
+ }
+ }
+#endif
+
+ /*
+ * If this is a slice desc. we need the first active solaris partition
+ * and if there isn't one then we need the first solaris partition.
+ */
+ if (desc->type == DM_SLICE) {
+ for (i = 0; i < FD_NUMPART; i++) {
+ if (iparts[i].bootid == ACTIVE &&
+ (iparts[i].systid == SUNIXOS ||
+ iparts[i].systid == SUNIXOS2)) {
+ break;
+ }
+ }
+
+ /* no active solaris part., try to get the first solaris part. */
+ if (i >= FD_NUMPART) {
+ for (i = 0; i < FD_NUMPART; i++) {
+ if (iparts[i].systid == SUNIXOS ||
+ iparts[i].systid == SUNIXOS2) {
+ break;
+ }
+ }
+ }
+
+ if (i < FD_NUMPART) {
+ /* we found a solaris partition to use */
+ char part_name[MAXPATHLEN];
+
+ if (conv_flag) {
+ /* convert part. name (e.g. c0d0p0) */
+ (void) snprintf(part_name, sizeof (part_name), "%s%d",
+ pname, i);
+ } else {
+ (void) snprintf(part_name, sizeof (part_name), "%d", i);
+ }
+
+ /* the media name comes from the slice desc. */
+ partitions[0] = cache_get_desc(DM_PARTITION, desc->p.disk,
+ part_name, desc->secondary_name, errp);
+ if (*errp != 0) {
+ cache_free_descriptors(partitions);
+ return (NULL);
+ }
+ partitions[1] = NULL;
+
+ return (partitions);
+
+ }
+
+ return (libdiskmgt_empty_desc_array(errp));
+ }
+
+ /* Must be for media, so get all the parts. */
+
+ pos = 0;
+ for (i = 0; i < FD_NUMPART; i++) {
+ if (iparts[i].systid != 0) {
+ char part_name[MAXPATHLEN];
+
+ if (conv_flag) {
+ /* convert part. name (e.g. c0d0p0) */
+ (void) snprintf(part_name, sizeof (part_name), "%s%d",
+ pname, i);
+ } else {
+ (void) snprintf(part_name, sizeof (part_name), "%d", i);
+ }
+
+ /* the media name comes from the media desc. */
+ partitions[pos] = cache_get_desc(DM_PARTITION, desc->p.disk,
+ part_name, desc->name, errp);
+ if (*errp != 0) {
+ cache_free_descriptors(partitions);
+ return (NULL);
+ }
+
+ pos++;
+ }
+ }
+ partitions[pos] = NULL;
+
+ *errp = 0;
+ return (partitions);
+}
+
+nvlist_t *
+partition_get_attributes(descriptor_t *dp, int *errp)
+{
+ nvlist_t *attrs = NULL;
+ struct ipart iparts[FD_NUMPART];
+
+ if (!desc_ok(dp)) {
+ *errp = ENODEV;
+ return (NULL);
+ }
+
+ if ((*errp = get_parts(dp->p.disk, iparts, NULL, 0)) != 0) {
+ return (NULL);
+ }
+
+ if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ if ((*errp = get_attrs(dp, iparts, attrs)) != 0) {
+ nvlist_free(attrs);
+ attrs = NULL;
+ }
+
+ return (attrs);
+}
+
+/*
+ * Look for the partition by the partition number (which is not too useful).
+ */
+descriptor_t *
+partition_get_descriptor_by_name(char *name, int *errp)
+{
+ descriptor_t **partitions;
+ int i;
+ descriptor_t *partition = NULL;
+
+ partitions = cache_get_descriptors(DM_PARTITION, errp);
+ if (*errp != 0) {
+ return (NULL);
+ }
+
+ for (i = 0; partitions[i]; i++) {
+ if (libdiskmgt_str_eq(name, partitions[i]->name)) {
+ partition = partitions[i];
+ } else {
+ /* clean up the unused descriptors */
+ cache_free_descriptor(partitions[i]);
+ }
+ }
+ free(partitions);
+
+ if (partition == NULL) {
+ *errp = ENODEV;
+ }
+
+ return (partition);
+}
+
+/* ARGSUSED */
+descriptor_t **
+partition_get_descriptors(int filter[], int *errp)
+{
+ return (cache_get_descriptors(DM_PARTITION, errp));
+}
+
+char *
+partition_get_name(descriptor_t *desc)
+{
+ return (desc->name);
+}
+
+/* ARGSUSED */
+nvlist_t *
+partition_get_stats(descriptor_t *dp, int stat_type, int *errp)
+{
+ /* There are no stat types defined for partitions */
+ *errp = EINVAL;
+ return (NULL);
+}
+
+/* ARGSUSED */
+int
+partition_has_fdisk(disk_t *dp, int fd)
+{
+ char bootsect[512 * 3]; /* 3 sectors to be safe */
+
+#ifdef sparc
+ if (dp->drv_type == DM_DT_FIXED) {
+ /* on sparc, only removable media can have fdisk parts. */
+ return (0);
+ }
+#endif
+
+ /*
+ * We assume the caller already made sure media was inserted and
+ * spun up.
+ */
+
+ if ((ioctl(fd, DKIOCGMBOOT, bootsect) < 0) && (errno != ENOTTY)) {
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * A partition descriptor points to a disk, the name is the partition number
+ * and the secondary name is the media name.
+ */
+int
+partition_make_descriptors()
+{
+ int error;
+ disk_t *dp;
+
+ dp = cache_get_disklist();
+ while (dp != NULL) {
+ struct ipart iparts[FD_NUMPART];
+ char pname[MAXPATHLEN];
+
+ if (get_parts(dp, iparts, pname, sizeof (pname)) == 0) {
+ int i;
+ char mname[MAXPATHLEN];
+ int conv_flag = 0;
+
+#ifdef i386
+ /* convert part. name (e.g. c0d0p0) */
+ int len;
+
+ len = strlen(pname);
+ if (len > 1 && *(pname + (len - 2)) == 'p') {
+ conv_flag = 1;
+ *(pname + (len - 1)) = 0;
+ }
+#endif
+
+ mname[0] = 0;
+ (void) media_read_name(dp, mname, sizeof (mname));
+
+ for (i = 0; i < FD_NUMPART; i++) {
+ if (iparts[i].systid != 0) {
+ char part_name[MAXPATHLEN];
+
+ if (conv_flag) {
+ /* convert part. name (e.g. c0d0p0) */
+ (void) snprintf(part_name, sizeof (part_name),
+ "%s%d", pname, i);
+ } else {
+ (void) snprintf(part_name, sizeof (part_name),
+ "%d", i);
+ }
+
+ cache_load_desc(DM_PARTITION, dp, part_name, mname,
+ &error);
+ if (error != 0) {
+ return (error);
+ }
+ }
+ }
+ }
+ dp = dp->next;
+ }
+
+ return (0);
+}
+
+static int
+get_attrs(descriptor_t *dp, struct ipart *iparts, nvlist_t *attrs)
+{
+ char *p;
+ int part_num;
+
+ /*
+ * We already made sure the media was loaded and ready in the
+ * get_parts call within partition_get_attributes.
+ */
+
+ p = strrchr(dp->name, 'p');
+ if (p == NULL) {
+ p = dp->name;
+ } else {
+ p++;
+ }
+ part_num = atoi(p);
+ if (part_num >= FD_NUMPART || iparts[part_num].systid == 0) {
+ return (ENODEV);
+ }
+
+ /* we found the partition */
+
+ if (nvlist_add_uint32(attrs, DM_BOOTID,
+ (unsigned int)iparts[part_num].bootid) != 0) {
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_uint32(attrs, DM_PTYPE,
+ (unsigned int)iparts[part_num].systid) != 0) {
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_uint32(attrs, DM_BHEAD,
+ (unsigned int)iparts[part_num].beghead) != 0) {
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_uint32(attrs, DM_BSECT,
+ (unsigned int)((iparts[part_num].begsect) & 0x3f)) != 0) {
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_uint32(attrs, DM_BCYL, (unsigned int)
+ ((iparts[part_num].begcyl & 0xff) |
+ ((iparts[part_num].begsect & 0xc0) << 2))) != 0) {
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_uint32(attrs, DM_EHEAD,
+ (unsigned int)iparts[part_num].endhead) != 0) {
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_uint32(attrs, DM_ESECT,
+ (unsigned int)((iparts[part_num].endsect) & 0x3f)) != 0) {
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_uint32(attrs, DM_ECYL, (unsigned int)
+ ((iparts[part_num].endcyl & 0xff) |
+ ((iparts[part_num].endsect & 0xc0) << 2))) != 0) {
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_uint32(attrs, DM_RELSECT,
+ (unsigned int)iparts[part_num].relsect) != 0) {
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_uint32(attrs, DM_NSECTORS,
+ (unsigned int)iparts[part_num].numsect) != 0) {
+ return (ENOMEM);
+ }
+
+ return (0);
+}
+
+static int
+get_parts(disk_t *disk, struct ipart *iparts, char *opath, int opath_len)
+{
+ int fd;
+ struct dk_minfo minfo;
+ struct mboot bootblk;
+ char bootsect[512];
+ int i;
+
+ /* Can't use drive_open_disk since we need the partition dev name. */
+ if ((fd = open_disk(disk, opath, opath_len)) < 0) {
+ return (ENODEV);
+ }
+
+ /* First make sure media is inserted and spun up. */
+ if (!media_read_info(fd, &minfo)) {
+#ifdef i386
+ /* XXX Work around bug 4725434 */
+ if (disk->removable) {
+#endif
+ (void) close(fd);
+ return (ENODEV);
+#ifdef i386
+ }
+#endif
+ }
+
+ if (!partition_has_fdisk(disk, fd)) {
+ (void) close(fd);
+ return (ENOTTY);
+ }
+
+ if (lseek(fd, 0, 0) == -1) {
+ (void) close(fd);
+ return (ENODEV);
+ }
+
+ if (read(fd, bootsect, 512) != 512) {
+ (void) close(fd);
+ return (ENODEV);
+ }
+ (void) close(fd);
+
+ (void) memcpy(&bootblk, bootsect, sizeof (bootblk));
+
+ if (les(bootblk.signature) != MBB_MAGIC) {
+ return (ENOTTY);
+ }
+
+ (void) memcpy(iparts, bootblk.parts, ISIZE);
+
+ for (i = 0; i < FD_NUMPART; i++) {
+ if (iparts[i].systid != 0) {
+ iparts[i].relsect = lel(iparts[i].relsect);
+ iparts[i].numsect = lel(iparts[i].numsect);
+ }
+ }
+
+ return (0);
+}
+/* return 1 if the partition descriptor is still valid, 0 if not. */
+static int
+desc_ok(descriptor_t *dp)
+{
+ /* First verify the media name for removable media */
+ if (dp->p.disk->removable) {
+ char mname[MAXPATHLEN];
+
+ if (!media_read_name(dp->p.disk, mname, sizeof (mname))) {
+ return (0);
+ }
+
+ if (mname[0] == 0) {
+ return (libdiskmgt_str_eq(dp->secondary_name, NULL));
+ } else {
+ return (libdiskmgt_str_eq(dp->secondary_name, mname));
+ }
+ }
+
+ /*
+ * We could verify the partition is still there but this is kind of
+ * expensive and other code down the line will do that (e.g. see
+ * get_attrs).
+ */
+
+ return (1);
+}
+
+/*
+ * Return 1 if partition has slices, 0 if not.
+ */
+static int
+has_slices(descriptor_t *desc, int *errp)
+{
+ int pnum;
+ int i;
+ char *p;
+ struct ipart iparts[FD_NUMPART];
+
+ if (get_parts(desc->p.disk, iparts, NULL, 0) != 0) {
+ *errp = ENODEV;
+ return (0);
+ }
+
+ p = strrchr(desc->name, 'p');
+ if (p == NULL) {
+ p = desc->name;
+ } else {
+ p++;
+ }
+ pnum = atoi(p);
+
+ /*
+ * Slices are associated with the active solaris partition or if there
+ * is no active solaris partition, then the first solaris partition.
+ */
+
+ *errp = 0;
+ if (iparts[pnum].bootid == ACTIVE &&
+ (iparts[pnum].systid == SUNIXOS ||
+ iparts[pnum].systid == SUNIXOS2)) {
+ return (1);
+ } else {
+ int active = 0;
+
+ /* Check if there are no active solaris partitions. */
+ for (i = 0; i < FD_NUMPART; i++) {
+ if (iparts[i].bootid == ACTIVE &&
+ (iparts[i].systid == SUNIXOS ||
+ iparts[i].systid == SUNIXOS2)) {
+ active = 1;
+ break;
+ }
+ }
+
+ if (!active) {
+ /* Check if this is the first solaris partition. */
+ for (i = 0; i < FD_NUMPART; i++) {
+ if (iparts[i].systid == SUNIXOS ||
+ iparts[i].systid == SUNIXOS2) {
+ break;
+ }
+ }
+
+ if (i < FD_NUMPART && i == pnum) {
+ return (1);
+ }
+ }
+ }
+
+ return (0);
+}
+
+static int
+open_disk(disk_t *diskp, char *opath, int len)
+{
+ char rmmedia_devpath[MAXPATHLEN];
+
+ if (diskp->removable && media_get_volm_path(diskp, rmmedia_devpath,
+ sizeof (rmmedia_devpath))) {
+
+ int fd;
+ struct stat buf;
+
+ if (rmmedia_devpath[0] == 0) {
+ /* removable but no media */
+ return (-1);
+ }
+
+ if ((fd = open(rmmedia_devpath, O_RDONLY|O_NDELAY)) < 0) {
+ return (-1);
+ }
+
+ if (fstat(fd, &buf) != 0) {
+ (void) close(fd);
+ return (-1);
+ }
+
+ if (buf.st_mode & S_IFCHR) {
+ /* opened, is device, so done */
+ if (opath != NULL) {
+ (void) strlcpy(opath, rmmedia_devpath, len);
+ }
+ return (fd);
+
+ } else if (buf.st_mode & S_IFDIR) {
+ /* disk w/ slices so handle the directory */
+ DIR *dirp;
+ struct dirent *dentp;
+ int dfd;
+#ifdef _LP64
+ struct dirent *result;
+#endif
+
+ /* each device file in the dir represents a slice */
+
+ if ((dirp = fdopendir(fd)) == NULL) {
+ (void) close(fd);
+ return (-1);
+ }
+
+ if ((dentp = (struct dirent *)malloc(sizeof (struct dirent) +
+ _PC_NAME_MAX + 1)) == NULL) {
+ /* out of memory */
+ (void) close(fd);
+ return (-1);
+ }
+#ifdef _LP64
+ while (readdir_r(dirp, dentp, &result) != NULL) {
+#else
+ while (readdir_r(dirp, dentp) != NULL) {
+#endif
+ char slice_path[MAXPATHLEN];
+
+ if (libdiskmgt_str_eq(".", dentp->d_name) ||
+ libdiskmgt_str_eq("..", dentp->d_name)) {
+ continue;
+ }
+
+ (void) snprintf(slice_path, sizeof (slice_path), "%s/%s",
+ rmmedia_devpath, dentp->d_name);
+
+ if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) < 0) {
+ continue;
+ }
+
+ if (fstat(dfd, &buf) == 0 && (buf.st_mode & S_IFCHR)) {
+ /* opened, is device, so done */
+ free(dentp);
+ (void) close(fd);
+ if (opath != NULL) {
+ (void) strlcpy(opath, slice_path, len);
+ }
+ return (dfd);
+ }
+
+ /* not a device, keep looking */
+ (void) close(dfd);
+ }
+
+ /* did not find a device under the rmmedia_path */
+ free(dentp);
+ (void) close(fd);
+ }
+
+ /* didn't find a device under volume management control */
+ return (-1);
+ }
+
+ /*
+ * Not removable media under volume management control so just open the
+ * first devpath.
+ */
+ if (diskp->aliases != NULL && diskp->aliases->devpaths != NULL) {
+#ifdef sparc
+ if (opath != NULL) {
+ (void) strlcpy(opath, diskp->aliases->devpaths->devpath, len);
+ }
+ return (open(diskp->aliases->devpaths->devpath, O_RDONLY|O_NDELAY));
+#else
+ /* On intel we need to open partition device (e.g. c0d0p0). */
+ char part_dev[MAXPATHLEN];
+ char *p;
+
+ (void) strlcpy(part_dev, diskp->aliases->devpaths->devpath,
+ sizeof (part_dev));
+ p = strrchr(part_dev, '/');
+ if (p == NULL) {
+ p = strrchr(part_dev, 's');
+ if (p != NULL) {
+ *p = 'p';
+ }
+ } else {
+ char *ps;
+
+ *p = 0;
+ ps = strrchr((p + 1), 's');
+ if (ps != NULL) {
+ *ps = 'p';
+ }
+ *p = '/';
+ }
+
+ if (opath != NULL) {
+ (void) strlcpy(opath, part_dev, len);
+ }
+ return (open(part_dev, O_RDONLY|O_NDELAY));
+#endif
+ }
+
+ return (-1);
+}
diff --git a/usr/src/lib/libdiskmgt/common/partition.h b/usr/src/lib/libdiskmgt/common/partition.h
new file mode 100644
index 0000000000..608f4c7ffe
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/partition.h
@@ -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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _PARTITION_H
+#define _PARTITION_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libdevinfo.h>
+#include <sys/dkio.h>
+#include <sys/dktp/fdisk.h>
+#include <devid.h>
+
+descriptor_t **partition_get_descriptors(int filter[], int *errp);
+descriptor_t **partition_get_assoc_descriptors(descriptor_t *desc,
+ dm_desc_type_t type, int *errp);
+descriptor_t **partition_get_assocs(descriptor_t *desc, int *errp);
+descriptor_t *partition_get_descriptor_by_name(char *name, int *errp);
+char *partition_get_name(descriptor_t *desc);
+nvlist_t *partition_get_attributes(descriptor_t *desc, int *errp);
+nvlist_t *partition_get_stats(descriptor_t *desc, int stat_type,
+ int *errp);
+int partition_has_fdisk(disk_t *dp, int fd);
+int partition_make_descriptors();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PARTITION_H */
diff --git a/usr/src/lib/libdiskmgt/common/path.c b/usr/src/lib/libdiskmgt/common/path.c
new file mode 100644
index 0000000000..b5080dc93b
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/path.c
@@ -0,0 +1,330 @@
+/*
+ * 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 <fcntl.h>
+#include <libdevinfo.h>
+#include <stdlib.h>
+#include <sys/sunddi.h>
+#include <sys/types.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+
+static int add_path_state(descriptor_t *dp, nvlist_t *attrs);
+static int add_wwn(descriptor_t *dp, nvlist_t *attrs);
+static descriptor_t **get_assoc_drives(descriptor_t *desc, int *errp);
+static descriptor_t **get_assoc_controllers(descriptor_t *desc, int *errp);
+static char *path_state_name(di_path_state_t st);
+
+descriptor_t **
+path_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type, int *errp)
+{
+ switch (type) {
+ case DM_DRIVE:
+ return (get_assoc_drives(desc, errp));
+ case DM_CONTROLLER:
+ return (get_assoc_controllers(desc, errp));
+ }
+
+ *errp = EINVAL;
+ return (NULL);
+}
+
+nvlist_t *
+path_get_attributes(descriptor_t *dp, int *errp)
+{
+ path_t *pp;
+ nvlist_t *attrs = NULL;
+
+ pp = dp->p.path;
+
+ if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ if (nvlist_add_string(attrs, DM_CTYPE, pp->ctype) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ /*
+ * We add the path state and wwn attributes only for descriptors that
+ * we got via their association to a specific drive (since these
+ * attributes are drive specific).
+ */
+ if (dp->name != NULL) {
+ if (add_path_state(dp, attrs) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+ if (add_wwn(dp, attrs) != 0) {
+ nvlist_free(attrs);
+ *errp = ENOMEM;
+ return (NULL);
+ }
+ }
+
+ *errp = 0;
+ return (attrs);
+}
+
+descriptor_t *
+path_get_descriptor_by_name(char *name, int *errp)
+{
+ descriptor_t **paths;
+ int i;
+ descriptor_t *path = NULL;
+
+ paths = cache_get_descriptors(DM_PATH, errp);
+ if (*errp != 0) {
+ return (NULL);
+ }
+
+ for (i = 0; paths[i]; i++) {
+ if (libdiskmgt_str_eq(name, paths[i]->p.path->name)) {
+ path = paths[i];
+ } else {
+ /* clean up the unused descriptors */
+ cache_free_descriptor(paths[i]);
+ }
+ }
+ free(paths);
+
+ if (path == NULL) {
+ *errp = ENODEV;
+ }
+
+ return (NULL);
+}
+
+/* ARGSUSED */
+descriptor_t **
+path_get_descriptors(int filter[], int *errp)
+{
+ return (cache_get_descriptors(DM_PATH, errp));
+}
+
+char *
+path_get_name(descriptor_t *desc)
+{
+ return (desc->p.path->name);
+}
+
+/* ARGSUSED */
+nvlist_t *
+path_get_stats(descriptor_t *dp, int stat_type, int *errp)
+{
+ /* There are no stat types defined for paths */
+ *errp = EINVAL;
+ return (NULL);
+}
+
+int
+path_make_descriptors()
+{
+ int error;
+ controller_t *cp;
+
+ cp = cache_get_controllerlist();
+ while (cp != NULL) {
+ if (cp->paths != NULL) {
+ int i;
+
+ for (i = 0; cp->paths[i]; i++) {
+ cache_load_desc(DM_PATH, cp->paths[i], NULL, NULL, &error);
+ if (error != 0) {
+ return (error);
+ }
+ }
+ }
+ cp = cp->next;
+ }
+
+ return (0);
+}
+
+/*
+ * This is called when we have a name in the descriptor name field. That
+ * only will be the case when the descriptor was created by getting the
+ * association from a drive to the path. Since we filled in the name with
+ * the drive device id in that case, we can use the device id to look up the
+ * drive-path state.
+ */
+static int
+add_path_state(descriptor_t *dp, nvlist_t *attrs)
+{
+ ddi_devid_t devid;
+ path_t *pp;
+ int i;
+ int status = 0;
+
+ if (devid_str_decode(dp->name, &devid, NULL) != 0) {
+ return (0);
+ }
+
+ /* find the index of the correct drive assoc. */
+ pp = dp->p.path;
+ for (i = 0; pp->disks[i] && pp->states[i] != -1; i++) {
+ if (pp->disks[i]->devid != NULL &&
+ devid_compare(pp->disks[i]->devid, devid) == 0) {
+
+ /* add the corresponding state */
+ if (nvlist_add_string(attrs, DM_PATH_STATE,
+ path_state_name(pp->states[i])) != 0) {
+ status = ENOMEM;
+ }
+ break;
+ }
+ }
+ devid_free(devid);
+
+ return (status);
+}
+
+/*
+ * This is called when we have a name in the descriptor name field. That
+ * only will be the case when the descriptor was created by getting the
+ * association from a drive to the path. Since we filled in the name with
+ * the drive device id in that case, we can use the device id to look up the
+ * drive wwn.
+ */
+static int
+add_wwn(descriptor_t *dp, nvlist_t *attrs)
+{
+ ddi_devid_t devid;
+ path_t *pp;
+ int i;
+ int status = 0;
+
+ if (devid_str_decode(dp->name, &devid, NULL) != 0) {
+ return (0);
+ }
+
+ /* find the index of the correct drive assoc. */
+ pp = dp->p.path;
+ for (i = 0; pp->disks[i] && pp->states[i] != -1; i++) {
+ if (pp->disks[i]->devid != NULL &&
+ devid_compare(pp->disks[i]->devid, devid) == 0) {
+
+ /* add the corresponding state */
+ if (nvlist_add_string(attrs, DM_WWN, pp->wwns[i]) != 0) {
+ status = ENOMEM;
+ }
+ break;
+ }
+ }
+ devid_free(devid);
+
+ return (status);
+}
+
+static descriptor_t **
+get_assoc_controllers(descriptor_t *desc, int *errp)
+{
+ path_t *pp;
+ descriptor_t **controllers;
+ int i;
+
+ pp = desc->p.path;
+
+ /* a path can have just one controller */
+
+ controllers = (descriptor_t **)calloc(2, sizeof (descriptor_t *));
+ if (controllers == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ i = 0;
+ if (pp->controller != NULL) {
+ controllers[i++] = cache_get_desc(DM_CONTROLLER,
+ pp->controller, NULL, NULL, errp);
+ if (*errp != 0) {
+ cache_free_descriptors(controllers);
+ return (NULL);
+ }
+ }
+
+ controllers[i] = NULL;
+
+ *errp = 0;
+ return (controllers);
+}
+
+static descriptor_t **
+get_assoc_drives(descriptor_t *desc, int *errp)
+{
+ path_t *pp;
+ descriptor_t **drives;
+ int cnt;
+ int i;
+
+ pp = desc->p.path;
+
+ /* Count how many we have. */
+ for (cnt = 0; pp->disks[cnt]; cnt++);
+
+ /* make the snapshot */
+ drives = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
+ if (drives == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ for (i = 0; pp->disks[i]; i++) {
+ drives[i] = cache_get_desc(DM_DRIVE, pp->disks[i], NULL, NULL,
+ errp);
+ if (*errp != 0) {
+ cache_free_descriptors(drives);
+ return (NULL);
+ }
+ }
+ drives[i] = NULL;
+
+ *errp = 0;
+ return (drives);
+}
+
+static char *
+path_state_name(di_path_state_t st)
+{
+ switch (st) {
+ case DI_PATH_STATE_ONLINE:
+ return ("online");
+ case DI_PATH_STATE_STANDBY:
+ return ("standby");
+ case DI_PATH_STATE_OFFLINE:
+ return ("offline");
+ case DI_PATH_STATE_FAULT:
+ return ("faulted");
+ }
+ return ("unknown");
+}
diff --git a/usr/src/lib/libdiskmgt/common/slice.c b/usr/src/lib/libdiskmgt/common/slice.c
new file mode 100644
index 0000000000..607efeb120
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/common/slice.c
@@ -0,0 +1,1399 @@
+/*
+ * 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 <fcntl.h>
+#include <libdevinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/dkio.h>
+#include <sys/stat.h>
+#include <sys/sunddi.h>
+#include <sys/types.h>
+#include <sys/vtoc.h>
+#include <unistd.h>
+#include <devid.h>
+#include <dirent.h>
+#include <sys/dktp/fdisk.h>
+#include <sys/efi_partition.h>
+
+#include "libdiskmgt.h"
+#include "disks_private.h"
+#include "partition.h"
+#ifndef VT_ENOTSUP
+#define VT_ENOTSUP (-5)
+#endif
+
+#define FMT_UNKNOWN 0
+#define FMT_VTOC 1
+#define FMT_EFI 2
+
+static struct inuse_detectors {
+ int (*detectorp)(char *slice, nvlist_t *attrs, int *errp);
+ char *used_by;
+} detectors[] = {
+ {inuse_mnt, DM_USE_MOUNT},
+ {inuse_svm, DM_USE_SVM},
+ {inuse_lu, DM_USE_LU},
+ {inuse_dump, DM_USE_DUMP},
+ {inuse_vxvm, DM_USE_VXVM},
+ {inuse_fs, DM_USE_FS}, /* fs should always be last */
+ {NULL, NULL}
+};
+
+static int add_inuse(char *name, nvlist_t *attrs);
+static int desc_ok(descriptor_t *dp);
+static void dsk2rdsk(char *dsk, char *rdsk, int size);
+static int get_attrs(descriptor_t *dp, int fd, nvlist_t *attrs);
+static descriptor_t **get_fixed_assocs(descriptor_t *desc, int *errp);
+static descriptor_t **get_removable_assocs(descriptor_t *desc, char *volm_path,
+ int *errp);
+static int get_slice_num(slice_t *devp);
+static int match_fixed_name(disk_t *dp, char *name, int *errp);
+static int match_removable_name(disk_t *dp, char *name, int *errp);
+static int make_fixed_descriptors(disk_t *dp);
+static int make_removable_descriptors(disk_t *dp);
+static int make_volm_dir_descriptors(disk_t *dp, int fd,
+ char *volm_path);
+static int num_removable_slices(int fd, struct stat *bufp,
+ char *volm_path);
+
+descriptor_t **
+slice_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type,
+ int *errp)
+{
+ if (!desc_ok(desc)) {
+ *errp = ENODEV;
+ return (NULL);
+ }
+
+ switch (type) {
+ case DM_MEDIA:
+ return (media_get_assocs(desc, errp));
+ case DM_PARTITION:
+ return (partition_get_assocs(desc, errp));
+ }
+
+ *errp = EINVAL;
+ return (NULL);
+}
+
+/*
+ * This is called by media/partition to get the slice descriptors for the given
+ * media/partition descriptor.
+ * For media, just get the slices, but for a partition, it must be a solaris
+ * partition and if there are active partitions, it must be the active one.
+ */
+descriptor_t **
+slice_get_assocs(descriptor_t *desc, int *errp)
+{
+ int under_volm = 0;
+ char volm_path[MAXPATHLEN];
+
+ /* Just check the first drive name. */
+ if (desc->p.disk->aliases == NULL) {
+ *errp = 0;
+ return (libdiskmgt_empty_desc_array(errp));
+ }
+
+ if (desc->p.disk->removable) {
+ if ((under_volm = media_get_volm_path(desc->p.disk, volm_path,
+ sizeof (volm_path)))) {
+ if (volm_path[0] == 0) {
+ /* no media */
+ *errp = 0;
+ return (libdiskmgt_empty_desc_array(errp));
+ }
+ }
+ }
+
+ if (desc->p.disk->removable) {
+ if (under_volm) {
+ return (get_removable_assocs(desc, volm_path, errp));
+ } else {
+ return (get_fixed_assocs(desc, errp));
+ }
+ } else {
+ return (get_fixed_assocs(desc, errp));
+ }
+}
+
+nvlist_t *
+slice_get_attributes(descriptor_t *dp, int *errp)
+{
+ nvlist_t *attrs = NULL;
+ int fd;
+ char devpath[MAXPATHLEN];
+
+ if (!desc_ok(dp)) {
+ *errp = ENODEV;
+ return (NULL);
+ }
+
+ if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ /* dp->name is /dev/dsk, need to convert back to /dev/rdsk */
+ dsk2rdsk(dp->name, devpath, sizeof (devpath));
+ fd = open(devpath, O_RDONLY|O_NDELAY);
+
+ if ((*errp = get_attrs(dp, fd, attrs)) != 0) {
+ nvlist_free(attrs);
+ attrs = NULL;
+ }
+
+ if (fd >= 0) {
+ (void) close(fd);
+ }
+
+ return (attrs);
+}
+
+/*
+ * Look for the slice by the slice devpath.
+ */
+descriptor_t *
+slice_get_descriptor_by_name(char *name, int *errp)
+{
+ int found = 0;
+ disk_t *dp;
+
+ for (dp = cache_get_disklist(); dp != NULL; dp = dp->next) {
+ if (dp->removable) {
+ found = match_removable_name(dp, name, errp);
+ } else {
+ found = match_fixed_name(dp, name, errp);
+ }
+
+ if (found) {
+ char mname[MAXPATHLEN];
+
+ if (*errp != 0) {
+ return (NULL);
+ }
+
+ mname[0] = 0;
+ (void) media_read_name(dp, mname, sizeof (mname));
+
+ return (cache_get_desc(DM_SLICE, dp, name, mname, errp));
+ }
+ }
+
+ *errp = ENODEV;
+ return (NULL);
+}
+
+/* ARGSUSED */
+descriptor_t **
+slice_get_descriptors(int filter[], int *errp)
+{
+ return (cache_get_descriptors(DM_SLICE, errp));
+}
+
+char *
+slice_get_name(descriptor_t *desc)
+{
+ return (desc->name);
+}
+
+nvlist_t *
+slice_get_stats(descriptor_t *dp, int stat_type, int *errp)
+{
+ nvlist_t *stats;
+ char *str;
+
+ if (stat_type != DM_SLICE_STAT_USE) {
+ *errp = EINVAL;
+ return (NULL);
+ }
+
+ *errp = 0;
+
+ if (nvlist_alloc(&stats, NVATTRS_STAT, 0) != 0) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ if ((*errp = add_inuse(dp->name, stats)) != 0) {
+ return (NULL);
+ }
+
+ /* if no cluster use, check for a use of the local name */
+ if (nvlist_lookup_string(stats, DM_USED_BY, &str) != 0) {
+ disk_t *diskp;
+
+ diskp = dp->p.disk;
+ if (diskp->aliases != NULL && diskp->aliases->cluster) {
+ slice_t *sp;
+ int snum = -1;
+ struct dk_minfo minfo;
+ struct dk_cinfo dkinfo;
+ char devpath[MAXPATHLEN];
+ int fd;
+
+ /* dp->name is /dev/dsk, need to convert back to /dev/rdsk */
+ dsk2rdsk(dp->name, devpath, sizeof (devpath));
+ fd = open(devpath, O_RDONLY|O_NDELAY);
+
+ if (fd >= 0 && media_read_info(fd, &minfo) &&
+ ioctl(fd, DKIOCINFO, &dkinfo) >= 0) {
+ snum = dkinfo.dki_partition;
+ }
+
+ if (fd >= 0) {
+ (void) close(fd);
+ }
+
+ if (snum >= 0) {
+ for (sp = diskp->aliases->orig_paths; sp != NULL;
+ sp = sp->next) {
+
+ if (sp->slice_num == snum) {
+ char localpath[MAXPATHLEN];
+
+ slice_rdsk2dsk(sp->devpath, localpath,
+ sizeof (localpath));
+
+ if ((*errp = add_inuse(localpath, stats)) != 0) {
+ return (NULL);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return (stats);
+}
+
+/*
+ * A slice descriptor points to a disk, the name is the devpath and the
+ * secondary name is the media name.
+ */
+int
+slice_make_descriptors()
+{
+ disk_t *dp;
+
+ dp = cache_get_disklist();
+ while (dp != NULL) {
+ int error;
+
+ if (dp->removable) {
+ error = make_removable_descriptors(dp);
+ } else {
+ error = make_fixed_descriptors(dp);
+ }
+ if (error != 0) {
+ return (error);
+ }
+
+ dp = dp->next;
+ }
+
+ return (0);
+}
+
+/* convert rdsk paths to dsk paths */
+void
+slice_rdsk2dsk(char *rdsk, char *dsk, int size)
+{
+ char *strp;
+
+ (void) strlcpy(dsk, rdsk, size);
+
+ if ((strp = strstr(dsk, "/rdsk/")) == NULL) {
+ /* not rdsk, check for floppy */
+ strp = strstr(dsk, "/rdiskette");
+ }
+
+ if (strp != NULL) {
+ strp++; /* move ptr to the r in rdsk or rdiskette */
+
+ /* move the succeeding chars over by one */
+ do {
+ *strp = *(strp + 1);
+ strp++;
+ } while (*strp);
+ }
+}
+
+/*
+ * Check if/how the slice is used.
+ */
+static int
+add_inuse(char *name, nvlist_t *attrs)
+{
+ int i;
+ int error;
+
+ for (i = 0; detectors[i].detectorp != NULL; i ++) {
+ if ((detectors[i].detectorp)(name, attrs, &error) || error != 0) {
+ if (error != 0) {
+ return (error);
+ }
+ break;
+ }
+ }
+
+ return (0);
+}
+
+/* return 1 if the slice descriptor is still valid, 0 if not. */
+static int
+desc_ok(descriptor_t *dp)
+{
+ /* First verify the media name for removable media */
+ if (dp->p.disk->removable) {
+ char mname[MAXPATHLEN];
+
+ if (!media_read_name(dp->p.disk, mname, sizeof (mname))) {
+ return (0);
+ }
+
+ if (mname[0] == 0) {
+ return (libdiskmgt_str_eq(dp->secondary_name, NULL));
+ } else {
+ return (libdiskmgt_str_eq(dp->secondary_name, mname));
+ }
+ }
+
+ /*
+ * We could verify the slice is still there, but other code down the
+ * line already does these checks (e.g. see get_attrs).
+ */
+
+ return (1);
+}
+
+/* convert dsk paths to rdsk paths */
+static void
+dsk2rdsk(char *dsk, char *rdsk, int size)
+{
+ char *slashp;
+ size_t len;
+
+ (void) strlcpy(rdsk, dsk, size);
+
+ /* make sure there is enough room to add the r to dsk */
+ len = strlen(dsk);
+ if (len + 2 > size) {
+ return;
+ }
+
+ if ((slashp = strstr(rdsk, "/dsk/")) == NULL) {
+ /* not dsk, check for floppy */
+ slashp = strstr(rdsk, "/diskette");
+ }
+
+ if (slashp != NULL) {
+ char *endp;
+
+ endp = rdsk + len; /* point to terminating 0 */
+ /* move the succeeding chars over by one */
+ do {
+ *(endp + 1) = *endp;
+ endp--;
+ } while (endp != slashp);
+
+ *(endp + 1) = 'r';
+ }
+}
+
+static int
+get_attrs(descriptor_t *dp, int fd, nvlist_t *attrs)
+{
+ struct dk_minfo minfo;
+ int status;
+ int data_format = FMT_UNKNOWN;
+ int snum = -1;
+ int error;
+ struct vtoc vtoc;
+ struct dk_gpt *efip;
+ struct dk_cinfo dkinfo;
+ disk_t *diskp;
+ char localpath[MAXPATHLEN];
+ int cooked_fd;
+ struct stat buf;
+ int mntpnt = 0;
+
+ if (fd < 0) {
+ return (ENODEV);
+ }
+
+ /* First make sure media is inserted and spun up. */
+ if (!media_read_info(fd, &minfo)) {
+#ifdef i386
+ /* XXX Work around bug 4725434 */
+ if (dp->p.disk->removable)
+#endif
+ return (ENODEV);
+ }
+
+ if ((status = read_vtoc(fd, &vtoc)) >= 0) {
+ data_format = FMT_VTOC;
+ } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) {
+ data_format = FMT_EFI;
+ if (nvlist_add_boolean(attrs, DM_EFI) != 0) {
+ efi_free(efip);
+ return (ENOMEM);
+ }
+ }
+
+ if (data_format == FMT_UNKNOWN) {
+ return (ENODEV);
+ }
+
+ if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) {
+ snum = dkinfo.dki_partition;
+ }
+
+ /* check the slice */
+ if (data_format == FMT_VTOC) {
+ if (snum < 0 || snum >= vtoc.v_nparts ||
+ vtoc.v_part[snum].p_size == 0) {
+ return (ENODEV);
+ }
+ } else { /* data_format == FMT_EFI */
+ if (snum < 0 || snum >= efip->efi_nparts ||
+ efip->efi_parts[snum].p_size == 0) {
+ efi_free(efip);
+ return (ENODEV);
+ }
+ }
+
+ /* the slice exists */
+
+ if (nvlist_add_uint32(attrs, DM_INDEX, snum) != 0) {
+ if (data_format == FMT_EFI) {
+ efi_free(efip);
+ }
+ return (ENOMEM);
+ }
+
+ if (data_format == FMT_VTOC) {
+ if (nvlist_add_uint64(attrs, DM_START, vtoc.v_part[snum].p_start)
+ != 0) {
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_uint64(attrs, DM_SIZE, vtoc.v_part[snum].p_size)
+ != 0) {
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_uint32(attrs, DM_TAG, vtoc.v_part[snum].p_tag)
+ != 0) {
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_uint32(attrs, DM_FLAG, vtoc.v_part[snum].p_flag)
+ != 0) {
+ return (ENOMEM);
+ }
+
+ } else { /* data_format == FMT_EFI */
+ if (nvlist_add_uint64(attrs, DM_START,
+ efip->efi_parts[snum].p_start) != 0) {
+ efi_free(efip);
+ return (ENOMEM);
+ }
+
+ if (nvlist_add_uint64(attrs, DM_SIZE, efip->efi_parts[snum].p_size)
+ != 0) {
+ efi_free(efip);
+ return (ENOMEM);
+ }
+
+ if (efip->efi_parts[snum].p_name[0] != 0) {
+ char label[EFI_PART_NAME_LEN + 1];
+
+ (void) snprintf(label, sizeof (label), "%.*s",
+ EFI_PART_NAME_LEN, efip->efi_parts[snum].p_name);
+ if (nvlist_add_string(attrs, DM_EFI_NAME, label) != 0) {
+ efi_free(efip);
+ return (ENOMEM);
+ }
+ }
+ }
+
+ if (data_format == FMT_EFI) {
+ efi_free(efip);
+ }
+
+ if (inuse_mnt(dp->name, attrs, &error)) {
+ if (error != 0) {
+ return (error);
+ }
+ mntpnt = 1;
+ }
+
+ /*
+ * Some extra attrs for cluster slices.
+ *
+ * get localname and possible mnt point for localpath
+ */
+ localpath[0] = 0;
+ diskp = dp->p.disk;
+ if (diskp->aliases != NULL && diskp->aliases->cluster) {
+ slice_t *sp;
+
+ for (sp = diskp->aliases->orig_paths; sp != NULL; sp = sp->next) {
+ if (sp->slice_num == -1) {
+ /* determine the slice number for this path */
+ int sfd;
+ struct dk_cinfo dkinfo;
+
+ if ((sfd = open(sp->devpath, O_RDONLY|O_NDELAY)) >= 0) {
+ if (ioctl(sfd, DKIOCINFO, &dkinfo) >= 0) {
+ sp->slice_num = dkinfo.dki_partition;
+ }
+ (void) close(sfd);
+ }
+ }
+
+ if (sp->slice_num == snum) {
+ slice_rdsk2dsk(sp->devpath, localpath, sizeof (localpath));
+
+ if (nvlist_add_string(attrs, DM_LOCALNAME, localpath)
+ != 0) {
+ return (ENOMEM);
+ }
+
+ if (mntpnt == 0) {
+ if (inuse_mnt(localpath, attrs, &error)) {
+ if (error != 0) {
+ return (error);
+ }
+ }
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (fstat(fd, &buf) != -1) {
+ if (nvlist_add_uint64(attrs, DM_DEVT, buf.st_rdev) != 0) {
+ return (ENOMEM);
+ }
+ }
+
+ /*
+ * We need to open the cooked slice (not the raw one) to get the
+ * correct devid. Also see if we need to read the localpath for the
+ * cluster disk, since the minor name is unavailable for the did pseudo
+ * device.
+ */
+ if (localpath[0] != 0) {
+ cooked_fd = open(localpath, O_RDONLY|O_NDELAY);
+ } else {
+ cooked_fd = open(dp->name, O_RDONLY|O_NDELAY);
+ }
+
+ if (cooked_fd >= 0) {
+ int no_mem = 0;
+ ddi_devid_t devid;
+
+ if (devid_get(cooked_fd, &devid) == 0) {
+ char *minor;
+
+ if (devid_get_minor_name(cooked_fd, &minor) == 0) {
+ char *devidstr;
+
+ if ((devidstr = devid_str_encode(devid, minor)) != 0) {
+
+ if (nvlist_add_string(attrs, DM_DEVICEID, devidstr)
+ != 0) {
+ no_mem = 1;
+ }
+
+ devid_str_free(devidstr);
+ }
+ devid_str_free(minor);
+ }
+ devid_free(devid);
+ }
+ (void) close(cooked_fd);
+
+ if (no_mem) {
+ return (ENOMEM);
+ }
+ }
+
+ return (0);
+}
+
+static descriptor_t **
+get_fixed_assocs(descriptor_t *desc, int *errp)
+{
+ int fd;
+ int status;
+ int data_format = FMT_UNKNOWN;
+ int cnt;
+ struct vtoc vtoc;
+ struct dk_gpt *efip;
+ int pos;
+ char *media_name = NULL;
+ slice_t *devp;
+ descriptor_t **slices;
+
+ if ((fd = drive_open_disk(desc->p.disk, NULL, 0)) < 0) {
+ *errp = ENODEV;
+ return (NULL);
+ }
+
+ if ((status = read_vtoc(fd, &vtoc)) >= 0) {
+ data_format = FMT_VTOC;
+ } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) {
+ data_format = FMT_EFI;
+ } else {
+ (void) close(fd);
+ *errp = 0;
+ return (libdiskmgt_empty_desc_array(errp));
+ }
+ (void) close(fd);
+
+ /* count the number of slices */
+ for (cnt = 0, devp = desc->p.disk->aliases->devpaths; devp != NULL;
+ devp = devp->next, cnt++);
+
+ /* allocate the array for the descriptors */
+ slices = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
+ if (slices == NULL) {
+ if (data_format == FMT_EFI) {
+ efi_free(efip);
+ }
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ /* get the media name from the descriptor */
+ if (desc->type == DM_MEDIA) {
+ media_name = desc->name;
+ } else {
+ /* must be a DM_PARTITION */
+ media_name = desc->secondary_name;
+ }
+
+ pos = 0;
+ for (devp = desc->p.disk->aliases->devpaths; devp != NULL;
+ devp = devp->next) {
+
+ int slice_num;
+ char devpath[MAXPATHLEN];
+
+ slice_num = get_slice_num(devp);
+ /* can't get slicenum, so no need to keep trying the drive */
+ if (slice_num == -1) {
+ break;
+ }
+
+ if (data_format == FMT_VTOC) {
+ if (slice_num >= vtoc.v_nparts ||
+ vtoc.v_part[slice_num].p_size == 0) {
+ continue;
+ }
+ } else { /* data_format == FMT_EFI */
+ if (slice_num >= efip->efi_nparts ||
+ efip->efi_parts[slice_num].p_size == 0) {
+ continue;
+ }
+ }
+
+ slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath));
+ slices[pos] = cache_get_desc(DM_SLICE, desc->p.disk, devpath,
+ media_name, errp);
+ if (*errp != 0) {
+ cache_free_descriptors(slices);
+ if (data_format == FMT_EFI) {
+ efi_free(efip);
+ }
+ return (NULL);
+ }
+ pos++;
+ }
+ slices[pos] = NULL;
+
+ if (data_format == FMT_EFI) {
+ efi_free(efip);
+ }
+
+ *errp = 0;
+ return (slices);
+}
+
+/*
+ * Called for loaded removable media under volume management control.
+ */
+static descriptor_t **
+get_removable_assocs(descriptor_t *desc, char *volm_path, int *errp)
+{
+ int pos;
+ int fd;
+ int cnt;
+ struct stat buf;
+ descriptor_t **slices;
+ char *media_name = NULL;
+ char devpath[MAXPATHLEN];
+
+ /* get the media name from the descriptor */
+ if (desc->type == DM_MEDIA) {
+ media_name = desc->name;
+ } else {
+ /* must be a DM_PARTITION */
+ media_name = desc->secondary_name;
+ }
+
+ /*
+ * For removable media under volm control the volm_path will
+ * either be a device (if the media is made up of a single slice) or
+ * a directory (if the media has multiple slices) with the slices
+ * as devices contained in the directory.
+ */
+
+ if ((fd = open(volm_path, O_RDONLY|O_NDELAY)) < 0 ||
+ fstat(fd, &buf) != 0) {
+ *errp = ENODEV;
+ return (NULL);
+ }
+
+ cnt = num_removable_slices(fd, &buf, volm_path);
+
+ /* allocate the array for the descriptors */
+ slices = (descriptor_t **)calloc(cnt + 1, sizeof (descriptor_t *));
+ if (slices == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ slice_rdsk2dsk(volm_path, devpath, sizeof (devpath));
+
+ pos = 0;
+ *errp = 0;
+ if (buf.st_mode & S_IFCHR) {
+ struct dk_minfo minfo;
+
+ /* Make sure media has readable label */
+ if (media_read_info(fd, &minfo)) {
+ int status;
+ int data_format = FMT_UNKNOWN;
+ struct vtoc vtoc;
+ struct dk_gpt *efip;
+
+ if ((status = read_vtoc(fd, &vtoc)) >= 0) {
+ data_format = FMT_VTOC;
+ } else if (status == VT_ENOTSUP &&
+ efi_alloc_and_read(fd, &efip) >= 0) {
+ data_format = FMT_EFI;
+ }
+
+ if (data_format != FMT_UNKNOWN) {
+ /* has a readable label */
+ slices[pos++] = cache_get_desc(DM_SLICE, desc->p.disk,
+ devpath, media_name, errp);
+ }
+ }
+
+ } else if (buf.st_mode & S_IFDIR) {
+ DIR *dirp;
+#ifdef _LP64
+ struct dirent *result;
+#endif
+
+ /* rewind, num_removable_slices already traversed */
+ (void) lseek(fd, 0, SEEK_SET);
+
+ if ((dirp = fdopendir(fd)) != NULL) {
+ struct dirent *dentp;
+
+ dentp = (struct dirent *)malloc(sizeof (struct dirent) +
+ _PC_NAME_MAX + 1);
+ if (dentp != NULL) {
+#ifdef _LP64
+ while (readdir_r(dirp, dentp, &result) != NULL) {
+#else
+ while (readdir_r(dirp, dentp) != NULL) {
+#endif
+ int dfd;
+ int is_dev = 0;
+ char slice_path[MAXPATHLEN];
+
+ if (libdiskmgt_str_eq(".", dentp->d_name) ||
+ libdiskmgt_str_eq("..", dentp->d_name)) {
+ continue;
+ }
+
+ (void) snprintf(slice_path, sizeof (slice_path),
+ "%s/%s", devpath, dentp->d_name);
+
+ if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) >= 0) {
+ struct stat buf;
+
+ if (fstat(dfd, &buf) == 0 &&
+ buf.st_mode & S_IFCHR) {
+ is_dev = 1;
+ }
+ (void) close(dfd);
+ }
+
+ if (!is_dev) {
+ continue;
+ }
+
+ slices[pos++] = cache_get_desc(DM_SLICE, desc->p.disk,
+ slice_path, media_name, errp);
+ if (*errp != 0) {
+ break;
+ }
+
+ }
+ free(dentp);
+ }
+ /* don't call closedir since it closes the fd */
+ }
+ }
+
+ (void) close(fd);
+
+ slices[pos] = NULL;
+
+ if (*errp != 0) {
+ cache_free_descriptors(slices);
+ return (NULL);
+ }
+
+ return (slices);
+}
+
+static int
+get_slice_num(slice_t *devp)
+{
+ /* check if we already determined the devpath slice number */
+ if (devp->slice_num == -1) {
+ int fd;
+
+ if ((fd = open(devp->devpath, O_RDONLY|O_NDELAY)) >= 0) {
+ struct dk_cinfo dkinfo;
+ if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) {
+ devp->slice_num = dkinfo.dki_partition;
+ }
+ (void) close(fd);
+ }
+ }
+
+ return (devp->slice_num);
+}
+
+static int
+make_fixed_descriptors(disk_t *dp)
+{
+ int error = 0;
+ alias_t *ap;
+ slice_t *devp;
+ char mname[MAXPATHLEN];
+ int data_format = FMT_UNKNOWN;
+ struct vtoc vtoc;
+ struct dk_gpt *efip;
+
+ /* Just check the first drive name. */
+ if ((ap = dp->aliases) == NULL) {
+ return (0);
+ }
+
+ mname[0] = 0;
+ (void) media_read_name(dp, mname, sizeof (mname));
+
+ for (devp = ap->devpaths; devp != NULL; devp = devp->next) {
+ int slice_num;
+ char devpath[MAXPATHLEN];
+
+ slice_num = get_slice_num(devp);
+ /* can't get slicenum, so no need to keep trying the drive */
+ if (slice_num == -1) {
+ break;
+ }
+
+ if (data_format == FMT_UNKNOWN) {
+ int fd;
+ int status;
+
+ if ((fd = drive_open_disk(dp, NULL, 0)) >= 0) {
+ if ((status = read_vtoc(fd, &vtoc)) >= 0) {
+ data_format = FMT_VTOC;
+ } else if (status == VT_ENOTSUP &&
+ efi_alloc_and_read(fd, &efip) >= 0) {
+ data_format = FMT_EFI;
+ }
+ (void) close(fd);
+ }
+ }
+
+ /* can't get slice data, so no need to keep trying the drive */
+ if (data_format == FMT_UNKNOWN) {
+ break;
+ }
+
+ if (data_format == FMT_VTOC) {
+ if (slice_num >= vtoc.v_nparts ||
+ vtoc.v_part[slice_num].p_size == 0) {
+ continue;
+ }
+ } else { /* data_format == FMT_EFI */
+ if (slice_num >= efip->efi_nparts ||
+ efip->efi_parts[slice_num].p_size == 0) {
+ continue;
+ }
+ }
+
+ slice_rdsk2dsk(devp->devpath, devpath, sizeof (devpath));
+ cache_load_desc(DM_SLICE, dp, devpath, mname, &error);
+ if (error != 0) {
+ break;
+ }
+ }
+
+ if (data_format == FMT_EFI) {
+ efi_free(efip);
+ }
+
+ return (error);
+}
+
+/*
+ * For removable media under volm control we have to do some special handling.
+ * We don't use the vtoc and /dev/dsk devpaths, since the slices are named
+ * under the /vol fs.
+ */
+static int
+make_removable_descriptors(disk_t *dp)
+{
+ char volm_path[MAXPATHLEN];
+ int error;
+ int fd;
+
+ /*
+ * If this removable drive is not under volm control, just use
+ * normal handling.
+ */
+ if (!media_get_volm_path(dp, volm_path, sizeof (volm_path))) {
+ return (make_fixed_descriptors(dp));
+ }
+
+ if (volm_path[0] == 0) {
+ /* no media */
+ return (0);
+ }
+
+ /*
+ * For removable media under volm control the rmmedia_devapth will
+ * either be a device (if the media is made up of a single slice) or
+ * a directory (if the media has multiple slices) with the slices
+ * as devices contained in the directory.
+ */
+ error = 0;
+ if ((fd = open(volm_path, O_RDONLY|O_NDELAY)) >= 0) {
+ struct stat buf;
+
+ if (fstat(fd, &buf) == 0) {
+ if (buf.st_mode & S_IFCHR) {
+ int status;
+ int data_format = FMT_UNKNOWN;
+ struct dk_minfo minfo;
+ int error;
+ struct vtoc vtoc;
+ struct dk_gpt *efip;
+ char devpath[MAXPATHLEN];
+
+ /* Make sure media has readable label */
+ if (!media_read_info(fd, &minfo)) {
+ /* no media */
+ return (0);
+ }
+
+ if ((status = read_vtoc(fd, &vtoc)) >= 0) {
+ data_format = FMT_VTOC;
+ } else if (status == VT_ENOTSUP &&
+ efi_alloc_and_read(fd, &efip) >= 0) {
+ data_format = FMT_EFI;
+ }
+
+ if (data_format == FMT_UNKNOWN) {
+ /* no readable label */
+ return (0);
+ }
+
+ slice_rdsk2dsk(volm_path, devpath, sizeof (devpath));
+ /* The media name is the volm_path in this case. */
+ cache_load_desc(DM_SLICE, dp, devpath, volm_path, &error);
+
+ } else if (buf.st_mode & S_IFDIR) {
+ /* each device file in the dir represents a slice */
+ error = make_volm_dir_descriptors(dp, fd, volm_path);
+ }
+ }
+ (void) close(fd);
+ }
+
+ return (error);
+}
+
+/*
+ * This handles removable media with slices under volume management control.
+ * In this case we have a dir which is the media name and each slice on the
+ * media is a device file in this dir.
+ */
+static int
+make_volm_dir_descriptors(disk_t *dp, int dirfd, char *volm_path)
+{
+ int error;
+ DIR *dirp;
+ struct dirent *dentp;
+#ifdef _LP64
+ struct dirent *result;
+#endif
+ char devpath[MAXPATHLEN];
+
+ if ((dirp = fdopendir(dirfd)) == NULL) {
+ return (0);
+ }
+
+ slice_rdsk2dsk(volm_path, devpath, sizeof (devpath));
+
+ error = 0;
+ dentp = (struct dirent *)malloc(sizeof (struct dirent) +
+ _PC_NAME_MAX + 1);
+ if (dentp != NULL) {
+#ifdef _LP64
+ while (readdir_r(dirp, dentp, &result) != NULL) {
+#else
+ while (readdir_r(dirp, dentp) != NULL) {
+#endif
+ int fd;
+ char slice_path[MAXPATHLEN];
+
+ if (libdiskmgt_str_eq(".", dentp->d_name) ||
+ libdiskmgt_str_eq("..", dentp->d_name)) {
+ continue;
+ }
+
+ (void) snprintf(slice_path, sizeof (slice_path), "%s/%s",
+ devpath, dentp->d_name);
+
+ if ((fd = open(slice_path, O_RDONLY|O_NDELAY)) >= 0) {
+ struct stat buf;
+
+ if (fstat(fd, &buf) == 0 && buf.st_mode & S_IFCHR) {
+ /* The media name is the volm_path in this case. */
+ cache_load_desc(DM_SLICE, dp, slice_path, volm_path,
+ &error);
+ if (error != 0) {
+ (void) close(fd);
+ break;
+ }
+ }
+
+ (void) close(fd);
+ }
+ }
+ free(dentp);
+ }
+ /* don't call closedir since it closes the fd */
+
+ return (error);
+}
+
+/*
+ * Just look for the name on the devpaths we have cached. Return 1 if we
+ * find the name and the size of that slice is non-zero.
+ */
+static int
+match_fixed_name(disk_t *diskp, char *name, int *errp)
+{
+ slice_t *dp = NULL;
+ alias_t *ap;
+ int slice_num;
+ int fd;
+ int status;
+ int data_format = FMT_UNKNOWN;
+ struct vtoc vtoc;
+ struct dk_gpt *efip;
+
+ ap = diskp->aliases;
+ while (ap != NULL) {
+ slice_t *devp;
+
+ devp = ap->devpaths;
+ while (devp != NULL) {
+ char path[MAXPATHLEN];
+
+ slice_rdsk2dsk(devp->devpath, path, sizeof (path));
+ if (libdiskmgt_str_eq(path, name)) {
+ /* found it */
+ dp = devp;
+ break;
+ }
+
+ devp = devp->next;
+ }
+
+ if (dp != NULL) {
+ break;
+ }
+
+ ap = ap->next;
+ }
+
+ if (dp == NULL) {
+ *errp = 0;
+ return (0);
+ }
+
+ /*
+ * If we found a match on the name we now have to check that this
+ * slice really exists (non-0 size).
+ */
+
+ slice_num = get_slice_num(dp);
+ /* can't get slicenum, so no slice */
+ if (slice_num == -1) {
+ *errp = ENODEV;
+ return (1);
+ }
+
+ if ((fd = drive_open_disk(diskp, NULL, 0)) < 0) {
+ *errp = ENODEV;
+ return (1);
+ }
+
+ if ((status = read_vtoc(fd, &vtoc)) >= 0) {
+ data_format = FMT_VTOC;
+ } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) {
+ data_format = FMT_EFI;
+ } else {
+ (void) close(fd);
+ *errp = ENODEV;
+ return (1);
+ }
+ (void) close(fd);
+
+ if (data_format == FMT_VTOC) {
+ if (slice_num < vtoc.v_nparts &&
+ vtoc.v_part[slice_num].p_size > 0) {
+ *errp = 0;
+ return (1);
+ }
+ } else { /* data_format == FMT_EFI */
+ if (slice_num < efip->efi_nparts &&
+ efip->efi_parts[slice_num].p_size > 0) {
+ efi_free(efip);
+ *errp = 0;
+ return (1);
+ }
+ efi_free(efip);
+ }
+
+ *errp = ENODEV;
+ return (1);
+}
+
+static int
+match_removable_name(disk_t *diskp, char *name, int *errp)
+{
+ char volm_path[MAXPATHLEN];
+ int found;
+ int fd;
+ struct stat buf;
+
+ /*
+ * If this removable drive is not under volm control, just use
+ * normal handling.
+ */
+ if (!media_get_volm_path(diskp, volm_path, sizeof (volm_path))) {
+ return (match_fixed_name(diskp, name, errp));
+ }
+
+ if (volm_path[0] == 0) {
+ /* no media */
+ *errp = 0;
+ return (0);
+ }
+
+ /*
+ * For removable media under volm control the rmmedia_devapth will
+ * either be a device (if the media is made up of a single slice) or
+ * a directory (if the media has multiple slices) with the slices
+ * as devices contained in the directory.
+ */
+
+ *errp = 0;
+
+ found = 0;
+ if ((fd = open(volm_path, O_RDONLY|O_NDELAY)) >= 0 &&
+ fstat(fd, &buf) == 0) {
+
+ if (buf.st_mode & S_IFCHR) {
+ char devpath[MAXPATHLEN];
+
+ slice_rdsk2dsk(volm_path, devpath, sizeof (devpath));
+ if (libdiskmgt_str_eq(name, devpath)) {
+ found = 1;
+ }
+
+ } else if (buf.st_mode & S_IFDIR) {
+ /* each device file in the dir represents a slice */
+ DIR *dirp;
+
+ if ((dirp = fdopendir(fd)) != NULL) {
+ struct dirent *dentp;
+#ifdef _LP64
+ struct dirent *result;
+#endif
+ char devpath[MAXPATHLEN];
+
+ slice_rdsk2dsk(volm_path, devpath, sizeof (devpath));
+
+ dentp = (struct dirent *)malloc(sizeof (struct dirent) +
+ _PC_NAME_MAX + 1);
+ if (dentp != NULL) {
+#ifdef _LP64
+ while (readdir_r(dirp, dentp, &result) != NULL) {
+#else
+ while (readdir_r(dirp, dentp) != NULL) {
+#endif
+ char slice_path[MAXPATHLEN];
+
+ if (libdiskmgt_str_eq(".", dentp->d_name) ||
+ libdiskmgt_str_eq("..", dentp->d_name)) {
+ continue;
+ }
+
+ (void) snprintf(slice_path, sizeof (slice_path),
+ "%s/%s", devpath, dentp->d_name);
+
+ if (libdiskmgt_str_eq(name, slice_path)) {
+ /* found name, check device */
+ int dfd;
+ int is_dev = 0;
+
+ if ((dfd = open(slice_path, O_RDONLY|O_NDELAY))
+ >= 0) {
+ struct stat buf;
+
+ if (fstat(dfd, &buf) == 0 &&
+ buf.st_mode & S_IFCHR) {
+ is_dev = 1;
+ }
+ (void) close(dfd);
+ }
+
+ /* we found the name */
+ found = 1;
+
+ if (!is_dev) {
+ *errp = ENODEV;
+ }
+
+ break;
+ }
+ }
+ free(dentp);
+ }
+ /* don't call closedir since it closes the fd */
+ }
+ } /* end of dir handling */
+
+ (void) close(fd);
+ }
+
+ return (found);
+}
+
+static int
+num_removable_slices(int fd, struct stat *bufp, char *volm_path)
+{
+ int cnt = 0;
+
+ if (bufp->st_mode & S_IFCHR) {
+ cnt = 1;
+
+ } else if (bufp->st_mode & S_IFDIR) {
+ /* each device file in the dir represents a slice */
+ DIR *dirp;
+
+ if ((dirp = fdopendir(fd)) != NULL) {
+ struct dirent *dentp;
+#ifdef _LP64
+ struct dirent *result;
+#endif
+ char devpath[MAXPATHLEN];
+
+ slice_rdsk2dsk(volm_path, devpath, sizeof (devpath));
+
+ dentp = (struct dirent *)malloc(sizeof (struct dirent) +
+ _PC_NAME_MAX + 1);
+ if (dentp != NULL) {
+#ifdef _LP64
+ while (readdir_r(dirp, dentp, &result) != NULL) {
+#else
+ while (readdir_r(dirp, dentp) != NULL) {
+#endif
+ int dfd;
+ char slice_path[MAXPATHLEN];
+
+ if (libdiskmgt_str_eq(".", dentp->d_name) ||
+ libdiskmgt_str_eq("..", dentp->d_name)) {
+ continue;
+ }
+
+ (void) snprintf(slice_path, sizeof (slice_path),
+ "%s/%s", devpath, dentp->d_name);
+
+ if ((dfd = open(slice_path, O_RDONLY|O_NDELAY)) >= 0) {
+ struct stat buf;
+
+ if (fstat(dfd, &buf) == 0 &&
+ buf.st_mode & S_IFCHR) {
+ cnt++;
+ }
+ (void) close(dfd);
+ }
+ }
+ free(dentp);
+ }
+ /* don't call closedir since it closes the fd */
+ }
+ } /* end of dir handling */
+
+ return (cnt);
+}
diff --git a/usr/src/lib/libdiskmgt/i386/Makefile b/usr/src/lib/libdiskmgt/i386/Makefile
new file mode 100644
index 0000000000..ee785147da
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/i386/Makefile
@@ -0,0 +1,32 @@
+#
+# 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 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libdiskmgt/i386/Makefile
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libdiskmgt/sparc/Makefile b/usr/src/lib/libdiskmgt/sparc/Makefile
new file mode 100644
index 0000000000..7cda8e1b48
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/sparc/Makefile
@@ -0,0 +1,32 @@
+#
+# 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 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libdiskmgt/sparc/Makefile
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libdiskmgt/sparcv9/Makefile b/usr/src/lib/libdiskmgt/sparcv9/Makefile
new file mode 100644
index 0000000000..f2593718fe
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/sparcv9/Makefile
@@ -0,0 +1,38 @@
+#
+# 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"
+#
+# lib/libdiskmgt/sparcv9/Makefile
+
+MAPDIR= ../spec/sparcv9
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libdiskmgt/spec/Makefile b/usr/src/lib/libdiskmgt/spec/Makefile
new file mode 100644
index 0000000000..7ec77b7139
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/spec/Makefile
@@ -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 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libdiskmgt/spec/Makefile
+
+include $(SRC)/lib/Makefile.spec.arch
diff --git a/usr/src/lib/libdiskmgt/spec/Makefile.targ b/usr/src/lib/libdiskmgt/spec/Makefile.targ
new file mode 100644
index 0000000000..3b1f0e9cc9
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/spec/Makefile.targ
@@ -0,0 +1,33 @@
+#
+# 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 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libdiskmgt/spec/Makefile.targ
+
+LIBRARY = libdiskmgt.a
+VERS = .1
+OBJECTS = diskmgt.o
+SPECCPP = -I../../common
diff --git a/usr/src/lib/libdiskmgt/spec/amd64/Makefile b/usr/src/lib/libdiskmgt/spec/amd64/Makefile
new file mode 100644
index 0000000000..5dd1b7240f
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/spec/amd64/Makefile
@@ -0,0 +1,42 @@
+#
+# 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"
+#
+
+include ../Makefile.targ
+
+# Add arch specific objects here
+OBJECTS +=
+
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.lib.64
+
+# Uncomment the following if the linker complains
+#amd64_C_PICFLAGS = $(amd64_C_BIGPICFLAGS)
+
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB64)
diff --git a/usr/src/lib/libdiskmgt/spec/diskmgt.spec b/usr/src/lib/libdiskmgt/spec/diskmgt.spec
new file mode 100644
index 0000000000..2d35464181
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/spec/diskmgt.spec
@@ -0,0 +1,111 @@
+#
+# 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"
+#
+# lib/libdiskmgt/spec/diskmgt.spec
+
+function dm_free_descriptors
+include <libdiskmgt.h>
+declaration void dm_free_descriptors(dm_descriptor_t *desc_list)
+version SUNWprivate_1.1
+end
+
+function dm_free_descriptor
+include <libdiskmgt.h>
+declaration void dm_free_descriptor(dm_descriptor_t desc)
+version SUNWprivate_1.1
+end
+
+function dm_free_name
+include <libdiskmgt.h>
+declaration void dm_free_name(char *name)
+version SUNWprivate_1.1
+end
+
+function dm_get_descriptors
+include <libdiskmgt.h>
+declaration dm_descriptor_t *dm_get_descriptors(dm_desc_type_t type, \
+ int filter[], int *errp)
+version SUNWprivate_1.1
+end
+
+function dm_get_associated_descriptors
+include <libdiskmgt.h>
+declaration dm_descriptor_t * \
+ dm_get_associated_descriptors(dm_descriptor_t desc, \
+ dm_desc_type_t type, int *errp)
+version SUNWprivate_1.1
+end
+
+function dm_get_associated_types
+include <libdiskmgt.h>
+declaration dm_desc_type_t *dm_get_associated_types(dm_desc_type_t type)
+version SUNWprivate_1.1
+end
+
+function dm_get_descriptor_by_name
+include <libdiskmgt.h>
+declaration dm_descriptor_t dm_get_descriptor_by_name(dm_desc_type_t \
+ desc_type, char *name, int *errp)
+version SUNWprivate_1.1
+end
+
+function dm_get_name
+include <libdiskmgt.h>
+declaration char *dm_get_name(dm_descriptor_t desc, int *errp)
+version SUNWprivate_1.1
+end
+
+function dm_get_type
+include <libdiskmgt.h>
+declaration dm_desc_type_t dm_get_type(dm_descriptor_t desc)
+version SUNWprivate_1.1
+end
+
+function dm_get_attributes
+include <libdiskmgt.h>
+declaration nvlist_t *dm_get_attributes(dm_descriptor_t desc, int *errp)
+version SUNWprivate_1.1
+end
+
+function dm_get_stats
+include <libdiskmgt.h>
+declaration nvlist_t *dm_get_stats(dm_descriptor_t desc, int stat_type, \
+ int *errp)
+version SUNWprivate_1.1
+end
+
+function dm_init_event_queue
+include <libdiskmgt.h>
+declaration void dm_init_event_queue(void(*callback)(nvlist_t *, int), \
+ int *errp)
+version SUNWprivate_1.1
+end
+
+function dm_get_event
+include <libdiskmgt.h>
+declaration nvlist_t *dm_get_event(int *errp)
+version SUNWprivate_1.1
+end
diff --git a/usr/src/lib/libdiskmgt/spec/i386/Makefile b/usr/src/lib/libdiskmgt/spec/i386/Makefile
new file mode 100644
index 0000000000..2548b2bdb4
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/spec/i386/Makefile
@@ -0,0 +1,36 @@
+#
+# 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 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libdiskmgt/spec/i386/Makefile
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB)
diff --git a/usr/src/lib/libdiskmgt/spec/sparc/Makefile b/usr/src/lib/libdiskmgt/spec/sparc/Makefile
new file mode 100644
index 0000000000..b1aa1cc40c
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/spec/sparc/Makefile
@@ -0,0 +1,36 @@
+#
+# 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 2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libdiskmgt/spec/sparc/Makefile
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB)
diff --git a/usr/src/lib/libdiskmgt/spec/sparcv9/Makefile b/usr/src/lib/libdiskmgt/spec/sparcv9/Makefile
new file mode 100644
index 0000000000..1d4ec142d6
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/spec/sparcv9/Makefile
@@ -0,0 +1,43 @@
+#
+# 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 2002-2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libdiskmgt/spec/sparcv9/Makefile
+
+include ../Makefile.targ
+
+# Add arch specific objects here
+OBJECTS +=
+
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.lib.64
+
+# Uncomment the following if the linker complains
+#sparcv9_C_PICFLAGS = -K PIC
+
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB64)
diff --git a/usr/src/lib/libdiskmgt/spec/versions b/usr/src/lib/libdiskmgt/spec/versions
new file mode 100644
index 0000000000..22e0dd1104
--- /dev/null
+++ b/usr/src/lib/libdiskmgt/spec/versions
@@ -0,0 +1,39 @@
+#
+# 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"
+#
+
+sparc {
+ SUNWprivate_1.1;
+}
+sparcv9 {
+ SUNWprivate_1.1;
+}
+i386 {
+ SUNWprivate_1.1;
+}
+amd64 {
+ SUNWprivate_1.1;
+}