root/usr/src/lib/libdiskmgt/common/entry.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#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 <libintl.h>
#include <locale.h>
#include <sys/debug.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/swap.h>

#include "libdiskmgt.h"
#include "disks_private.h"
#include "partition.h"

#define ANY_ZPOOL_USE(who) \
        (((who) == DM_WHO_ZPOOL_FORCE) || \
        ((who) == DM_WHO_ZPOOL) || \
        ((who) == DM_WHO_ZPOOL_SPARE))

extern  char    *getfullblkname();

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);
static int build_usage_string(char *dname, char *by, char *data, char **use,
        int *found, int *errp);

void
dm_free_descriptor(dm_descriptor_t desc)
{
        descriptor_t    *dp;

        if (desc == 0) {
                return;
        }
        dp = (descriptor_t *)(uintptr_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 *)(uintptr_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 *)(uintptr_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 = 0;


        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 *)(uintptr_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 *)(uintptr_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:
                if (stat_type == DM_SLICE_STAT_USE) {
                        /*
                         * If NOINUSE_CHECK is set, we do not perform
                         * the in use checking if the user has set stat_type
                         * DM_SLICE_STAT_USE
                         */
                        if (NOINUSE_SET) {
                                stats = NULL;
                                break;
                        }
                }
                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 *)(uintptr_t)desc;

        cache_rlock();

        if (!cache_is_valid_desc(dp)) {
                cache_unlock();
                return (-1);
        }

        cache_unlock();

        return (dp->type);
}
/*
 * Returns, via slices paramater, a dm_descriptor_t list of
 * slices for the named disk drive.
 */
void
dm_get_slices(char *drive, dm_descriptor_t **slices, int *errp)
{
        dm_descriptor_t alias;
        dm_descriptor_t *media;
        dm_descriptor_t *disk;

        *slices = NULL;
        *errp = 0;

        if (drive == NULL) {
                return;
        }

        alias = dm_get_descriptor_by_name(DM_ALIAS, drive, errp);

        /*
         * Errors must be handled by the caller. The dm_descriptor_t *
         * values will be NULL if an error occured in these calls.
         */

        if (alias != 0) {
                disk = dm_get_associated_descriptors(alias, DM_DRIVE, errp);
                dm_free_descriptor(alias);
                if (disk != NULL) {
                        media = dm_get_associated_descriptors(*disk,
                            DM_MEDIA, errp);
                        dm_free_descriptors(disk);
                        if (media != NULL) {
                                *slices = dm_get_associated_descriptors(*media,
                                    DM_SLICE, errp);
                                dm_free_descriptors(media);
                        }
                }
        }
}
/*
 * Convenience function to get slice stats
 */
void
dm_get_slice_stats(char *slice, nvlist_t **dev_stats, int *errp)
{
        dm_descriptor_t devp;

        *dev_stats = NULL;
        *errp = 0;

        if (slice == NULL) {
                return;
        }

        /*
         * Errors must be handled by the caller. The dm_descriptor_t *
         * values will be NULL if an error occured in these calls.
         */
        devp = dm_get_descriptor_by_name(DM_SLICE, slice, errp);
        if (devp != 0) {
                *dev_stats = dm_get_stats(devp, DM_SLICE_STAT_USE, errp);
                dm_free_descriptor(devp);
        }
}

/*
 * Checks for overlapping slices.   If the given device is a slice, and it
 * overlaps with any non-backup slice on the disk, return true with a detailed
 * description similar to dm_inuse().
 */
int
dm_isoverlapping(char *slicename, char **overlaps_with, int *errp)
{
        dm_descriptor_t slice = 0;
        dm_descriptor_t *media = NULL;
        dm_descriptor_t *slices = NULL;
        int             i = 0;
        uint32_t        in_snum;
        uint64_t        start_block = 0;
        uint64_t        end_block = 0;
        uint64_t        media_size = 0;
        uint64_t        size = 0;
        nvlist_t        *media_attrs = NULL;
        nvlist_t        *slice_attrs = NULL;
        int             ret = 0;

        slice = dm_get_descriptor_by_name(DM_SLICE, slicename, errp);
        if (slice == 0)
                goto out;

        /*
         * Get the list of slices be fetching the associated media, and then all
         * associated slices.
         */
        media = dm_get_associated_descriptors(slice, DM_MEDIA, errp);
        if (media == NULL || *media == 0 || *errp != 0)
                goto out;

        slices = dm_get_associated_descriptors(*media, DM_SLICE, errp);
        if (slices == NULL || *slices == 0 || *errp != 0)
                goto out;

        media_attrs = dm_get_attributes(*media, errp);
        if (media_attrs == NULL || *errp)
                goto out;

        *errp = nvlist_lookup_uint64(media_attrs, DM_NACCESSIBLE, &media_size);
        if (*errp != 0)
                goto out;

        slice_attrs = dm_get_attributes(slice, errp);
        if (slice_attrs == NULL || *errp != 0)
                goto out;

        *errp = nvlist_lookup_uint64(slice_attrs, DM_START, &start_block);
        if (*errp != 0)
                goto out;

        *errp = nvlist_lookup_uint64(slice_attrs, DM_SIZE, &size);
        if (*errp != 0)
                goto out;

        *errp = nvlist_lookup_uint32(slice_attrs, DM_INDEX, &in_snum);
        if (*errp != 0)
                goto out;

        end_block = (start_block + size) - 1;

        for (i = 0; slices[i]; i ++) {
                uint64_t other_start;
                uint64_t other_end;
                uint64_t other_size;
                uint32_t snum;

                nvlist_t *other_attrs = dm_get_attributes(slices[i], errp);

                if (other_attrs == NULL)
                        continue;

                if (*errp != 0)
                        goto out;

                *errp = nvlist_lookup_uint64(other_attrs, DM_START,
                    &other_start);
                if (*errp) {
                        nvlist_free(other_attrs);
                        goto out;
                }

                *errp = nvlist_lookup_uint64(other_attrs, DM_SIZE,
                    &other_size);

                if (*errp) {
                        nvlist_free(other_attrs);
                        ret = -1;
                        goto out;
                }

                other_end = (other_size + other_start) - 1;

                *errp = nvlist_lookup_uint32(other_attrs, DM_INDEX,
                    &snum);

                if (*errp) {
                        nvlist_free(other_attrs);
                        ret = -1;
                        goto out;
                }

                /*
                 * Check to see if there are > 2 overlapping regions
                 * on this media in the same region as this slice.
                 * This is done by assuming the following:
                 *      Slice 2 is the backup slice if it is the size
                 *      of the whole disk
                 * If slice 2 is the overlap and slice 2 is the size of
                 * the whole disk, continue. If another slice is found
                 * that overlaps with our slice, return it.
                 * There is the potential that there is more than one slice
                 * that our slice overlaps with, however, we only return
                 * the first overlapping slice we find.
                 *
                 */
                if (start_block >= other_start && start_block <= other_end) {
                        if ((snum == 2 && (other_size == media_size)) ||
                            snum == in_snum) {
                                continue;
                        } else {
                                char *str = dm_get_name(slices[i], errp);
                                if (*errp != 0) {
                                        nvlist_free(other_attrs);
                                        ret = -1;
                                        goto out;
                                }
                                *overlaps_with = strdup(str);
                                dm_free_name(str);
                                nvlist_free(other_attrs);
                                ret = 1;
                                goto out;
                        }
                } else if (other_start >= start_block &&
                    other_start <= end_block) {
                        if ((snum == 2 && (other_size == media_size)) ||
                            snum == in_snum) {
                                continue;
                        } else {
                                char *str = dm_get_name(slices[i], errp);
                                if (*errp != 0) {
                                        nvlist_free(other_attrs);
                                        ret = -1;
                                        goto out;
                                }
                                *overlaps_with = strdup(str);
                                dm_free_name(str);
                                nvlist_free(other_attrs);
                                ret = 1;
                                goto out;
                        }
                }
                nvlist_free(other_attrs);
        }

out:
        nvlist_free(media_attrs);
        nvlist_free(slice_attrs);

        if (slices)
                dm_free_descriptors(slices);
        if (media)
                dm_free_descriptors(media);
        if (slice)
                dm_free_descriptor(slice);

        return (ret);
}

/*
 * Get the full list of swap entries.  Returns -1 on error, or >= 0 to
 * indicate the number of entries in the list.  Callers are responsible
 * for calling dm_free_swapentries() to deallocate memory.  If this
 * returns 0, the swaptbl_t still needs to be freed.
 */
int
dm_get_swapentries(swaptbl_t **stp, int *errp)
{
        int count, i;
        swaptbl_t *tbl;
        char *ptr;

        *stp = NULL;

        /* get number of swap entries */
        if ((count = swapctl(SC_GETNSWP, NULL)) < 0) {
                *errp = errno;
                return (-1);
        }

        if (count == 0) {
                return (0);
        }

        /* allocate space */
        tbl = calloc(1, sizeof (int) + count * sizeof (swapent_t));
        if (tbl == NULL) {
                *errp = ENOMEM;
                return (-1);
        }

        ptr = calloc(1, count * MAXPATHLEN);
        if (ptr == NULL) {
                *errp = ENOMEM;
                free(tbl);
                return (-1);
        }

        /* set up pointers to the pathnames */
        tbl->swt_n = count;
        for (i = 0; i < count; i++) {
                tbl->swt_ent[i].ste_path = ptr;
                ptr += MAXPATHLEN;
        }

        /* get list of swap paths */
        count = swapctl(SC_LIST, tbl);
        if (count < 0) {
                *errp = errno;
                free(ptr);
                free(tbl);
                return (-1);
        }

        *stp = tbl;
        return (count);
}

/* ARGSUSED */
void
dm_free_swapentries(swaptbl_t *stp)
{
        ASSERT(stp != NULL);

        free(stp->swt_ent[0].ste_path);
        free(stp);
}

/*
 * Check a slice to see if it's being used by swap.
 */
int
dm_inuse_swap(const char *dev_name, int *errp)
{
        int count;
        int found;
        swaptbl_t *tbl = NULL;

        *errp = 0;

        count = dm_get_swapentries(&tbl, errp);
        if (count < 0 || *errp) {
                if (tbl)
                        dm_free_swapentries(tbl);
                return (-1);
        }

        /* if there are no swap entries, we're done */
        if (!count) {
                return (0);
        }

        ASSERT(tbl != NULL);

        found = 0;
        while (count--) {
                if (strcmp(dev_name, tbl->swt_ent[count].ste_path) == 0) {
                        found = 1;
                        break;
                }
        }

        dm_free_swapentries(tbl);
        return (found);
}

/*
 * Returns 'in use' details, if found, about a specific dev_name,
 * based on the caller(who). It is important to note that it is possible
 * for there to be more than one 'in use' statistic regarding a dev_name.
 * The **msg parameter returns a list of 'in use' details. This message
 * is formatted via gettext().
 */
int
dm_inuse(char *dev_name, char **msg, dm_who_type_t who, int *errp)
{
        nvlist_t *dev_stats = NULL;
        char *by, *data;
        nvpair_t *nvwhat = NULL;
        nvpair_t *nvdesc = NULL;
        int     found = 0;
        int     err;
        char    *dname = NULL;

        *errp = 0;
        *msg = NULL;

        /*
         * If the user doesn't want to do in use checking, return.
         */

        if (NOINUSE_SET)
                return (0);

        dname = getfullblkname(dev_name);
        /*
         * If we cannot find the block name, we cannot check the device
         * for in use statistics. So, return found, which is == 0.
         */
        if (dname == NULL || *dname == '\0') {
                return (found);
        }

        /*
         * Slice stats for swap devices are only returned if mounted
         * (e.g. /tmp).  Other devices or files being used for swap
         * are ignored, so we add a special check here to use swapctl(2)
         * to perform in-use checking.
         */
        if (ANY_ZPOOL_USE(who) && (err = dm_inuse_swap(dname, errp))) {

                /* on error, dm_inuse_swap sets errp */
                if (err < 0) {
                        free(dname);
                        return (err);
                }

                /* simulate a mounted swap device */
                (void) build_usage_string(dname, DM_USE_MOUNT, "swap", msg,
                    &found, errp);

                /* if this fails, dm_get_usage_string changed */
                ASSERT(found == 1);

                free(dname);
                return (found);
        }

        dm_get_slice_stats(dname, &dev_stats, errp);
        if (dev_stats == NULL) {
                /*
                 * If there is an error, but it isn't a no device found error
                 * return the error as recorded. Otherwise, with a full
                 * block name, we might not be able to get the slice
                 * associated, and will get an ENODEV error.
                 */
                if (*errp == ENODEV)
                        *errp = 0;
                free(dname);
                return (found);
        }

        for (;;) {

                nvwhat = nvlist_next_nvpair(dev_stats, nvdesc);
                nvdesc = nvlist_next_nvpair(dev_stats, nvwhat);

                /*
                 * End of the list found.
                 */
                if (nvwhat == NULL || nvdesc == NULL) {
                        break;
                }
                /*
                 * Otherwise, we check to see if this client(who) cares
                 * about this in use scenario
                 */

                ASSERT(strcmp(nvpair_name(nvwhat), DM_USED_BY) == 0);
                ASSERT(strcmp(nvpair_name(nvdesc), DM_USED_NAME) == 0);
                /*
                 * If we error getting the string value continue on
                 * to the next pair(if there is one)
                 */
                if (nvpair_value_string(nvwhat, &by)) {
                        continue;
                }
                if (nvpair_value_string(nvdesc, &data)) {
                        continue;
                }

                switch (who) {
                        case DM_WHO_MKFS:
                                /*
                                 * mkfs is not in use for these cases.
                                 * All others are in use.
                                 */
                                if (strcmp(by, DM_USE_LU) == 0 ||
                                    strcmp(by, DM_USE_FS) == 0 ||
                                    strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0) {
                                        break;
                                }
                                if (build_usage_string(dname,
                                    by, data, msg, &found, errp) != 0) {
                                        if (*errp) {
                                                goto out;
                                        }
                                }
                                break;
                        case DM_WHO_SWAP:
                                /*
                                 * Not in use for this.
                                 */
                                if (strcmp(by, DM_USE_DUMP) == 0 ||
                                    strcmp(by, DM_USE_FS) == 0 ||
                                    strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0) {
                                        break;
                                }
                                if (strcmp(by, DM_USE_LU) == 0 &&
                                    strcmp(data, "-") == 0) {
                                        break;
                                }
                                if (strcmp(by, DM_USE_VFSTAB) == 0 &&
                                    strcmp(data, "") == 0) {
                                        break;
                                }
                                if (build_usage_string(dname,
                                    by, data, msg, &found, errp) != 0) {
                                        if (*errp) {
                                                goto out;
                                        }
                                }
                                break;
                        case DM_WHO_DUMP:
                                /*
                                 * Not in use for this.
                                 */
                                if ((strcmp(by, DM_USE_MOUNT) == 0 &&
                                    strcmp(data, "swap") == 0) ||
                                    strcmp(by, DM_USE_DUMP) == 0 ||
                                    strcmp(by, DM_USE_FS) == 0 ||
                                    strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0) {
                                        break;
                                }
                                if (build_usage_string(dname,
                                    by, data, msg, &found, errp)) {
                                        if (*errp) {
                                                goto out;
                                        }
                                }
                                break;

                        case DM_WHO_FORMAT:
                                if (strcmp(by, DM_USE_FS) == 0 ||
                                    strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0)
                                        break;
                                if (build_usage_string(dname,
                                    by, data, msg, &found, errp) != 0) {
                                        if (*errp) {
                                                goto out;
                                        }
                                }
                                break;

                        case DM_WHO_ZPOOL_FORCE:
                                if (strcmp(by, DM_USE_FS) == 0 ||
                                    strcmp(by, DM_USE_EXPORTED_ZPOOL) == 0)
                                        break;
                                /* FALLTHROUGH */
                        case DM_WHO_ZPOOL:
                                if (build_usage_string(dname,
                                    by, data, msg, &found, errp) != 0) {
                                        if (*errp)
                                                goto out;
                                }
                                break;

                        case DM_WHO_ZPOOL_SPARE:
                                if (strcmp(by, DM_USE_SPARE_ZPOOL) != 0) {
                                        if (build_usage_string(dname, by,
                                            data, msg, &found, errp) != 0) {
                                                if (*errp)
                                                        goto out;
                                        }
                                }
                                break;

                        default:
                                /*
                                 * nothing found in use for this client
                                 * of libdiskmgt. Default is 'not in use'.
                                 */
                                break;
                }
        }
out:
        if (dname != NULL)
                free(dname);
        nvlist_free(dev_stats);

        return (found);
}

void
dm_get_usage_string(char *what, char *how, char **usage_string)
{


        if (usage_string == NULL || what == NULL) {
                return;
        }
        *usage_string = NULL;

        if (strcmp(what, DM_USE_MOUNT) == 0) {
                if (strcmp(how, "swap") == 0) {
                        *usage_string = dgettext(TEXT_DOMAIN,
                            "%s is currently used by swap. Please see swap(8)."
                            "\n");
                } else {
                        *usage_string = dgettext(TEXT_DOMAIN,
                            "%s is currently mounted on %s."
                            " Please see umount(8).\n");
                }
        } else if (strcmp(what, DM_USE_VFSTAB) == 0) {
                *usage_string = dgettext(TEXT_DOMAIN,
                    "%s is normally mounted on %s according to /etc/vfstab. "
                    "Please remove this entry to use this device.\n");
        } else if (strcmp(what, DM_USE_FS) == 0) {
                *usage_string = dgettext(TEXT_DOMAIN,
                    "%s contains a %s filesystem.\n");
        } else if (strcmp(what, DM_USE_VXVM) == 0) {
                *usage_string = dgettext(TEXT_DOMAIN,
                    "%s is part of VxVM volume %s.\n");
        } else if (strcmp(what, DM_USE_LU) == 0) {
                *usage_string = dgettext(TEXT_DOMAIN,
                    "%s is in use for live upgrade %s. Please see ludelete(8)."
                    "\n");
        } else if (strcmp(what, DM_USE_DUMP) == 0) {
                *usage_string = dgettext(TEXT_DOMAIN,
                    "%s is in use by %s. Please see dumpadm(8)."
                    "\n");
        } else if (strcmp(what, DM_USE_EXPORTED_ZPOOL) == 0) {
                *usage_string = dgettext(TEXT_DOMAIN,
                    "%s is part of exported or potentially active ZFS pool %s. "
                    "Please see zpool(8).\n");
        } else if (strcmp(what, DM_USE_ACTIVE_ZPOOL) == 0) {
                *usage_string = dgettext(TEXT_DOMAIN,
                    "%s is part of active ZFS pool %s. Please see zpool(8)."
                    "\n");
        } else if (strcmp(what, DM_USE_SPARE_ZPOOL) == 0) {
                *usage_string = dgettext(TEXT_DOMAIN,
                    "%s is reserved as a hot spare for ZFS pool %s.  Please "
                    "see zpool(8).\n");
        } else if (strcmp(what, DM_USE_L2CACHE_ZPOOL) == 0) {
                *usage_string = dgettext(TEXT_DOMAIN,
                    "%s is in use as a cache device for ZFS pool %s.  "
                    "Please see zpool(8).\n");
        }
}
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 *)(uintptr_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
}
/*
 * Build the usage string for the in use data. Return the build string in
 * the msg parameter. This function takes care of reallocing all the memory
 * for this usage string. Usage string is returned already formatted for
 * localization.
 */
static int
build_usage_string(char *dname, char *by, char *data, char **msg,
    int *found, int *errp)
{
        int     len0;
        int     len1;
        char    *use;
        char    *p;

        *errp = 0;

        dm_get_usage_string(by, data, &use);
        if (!use) {
                return (-1);
        }

        if (*msg)
                len0 = strlen(*msg);
        else
                len0 = 0;
        /* LINTED */
        len1 = snprintf(NULL, 0, use, dname, data);

        /*
         * If multiple in use details they
         * are listed 1 per line for ease of
         * reading. dm_find_usage_string
         * formats these appropriately.
         */
        if ((p = realloc(*msg, len0 + len1 + 1)) == NULL) {
                *errp = errno;
                free(*msg);
                return (-1);
        }
        *msg = p;

        /* LINTED */
        (void) snprintf(*msg + len0, len1 + 1, use, dname, data);
        (*found)++;
        return (0);
}