view usr/src/lib/libdiskmgt/common/entry.c @ 3741:806e933bf124

6441384 zpool import action message is not correctly localized 6473418 setting user defined property on full filesystem should report error 6513953 Unable to create global hotspare in ja locale
author mmusante
date Thu, 01 Mar 2007 06:09:49 -0800
parents 76b439ec3ac1
children 2e3825fd8bb5
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2007 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 <libintl.h>
#include <locale.h>
#include <sys/debug.h>

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

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 == NULL) {
	    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 = 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 *)(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 != NULL) {
		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 != NULL) {
		*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 = NULL;
	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 == NULL)
		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 == NULL || *errp != 0)
		goto out;

	slices = dm_get_associated_descriptors(*media, DM_SLICE, errp);
	if (slices == NULL || *slices == NULL || *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:
	if (media_attrs)
		nvlist_free(media_attrs);
	if (slice_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);
}

/*
 * 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;
	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);
	}

	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. For example,
		 * an SVM metadevice will return a value from getfullblkname()
		 * but libdiskmgt won't be able to find this device for
		 * statistics gathering. This is expected and we should not
		 * report errnoneous errors.
		 */
		if (*errp) {
			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 (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);
	if (dev_stats != NULL)
		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(1M)."
			    "\n");
		} else {
			*usage_string = dgettext(TEXT_DOMAIN,
			    "%s is currently mounted on %s."
			    " Please see umount(1M).\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_SVM) == 0) {
		if (strcmp(how, "mdb") == 0) {
			*usage_string = dgettext(TEXT_DOMAIN,
			    "%s contains an SVM %s. Please see "
			    "metadb(1M).\n");
		} else {
			*usage_string = dgettext(TEXT_DOMAIN,
			    "%s is part of SVM volume %s. "
			    "Please see metaclear(1M).\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(1M)."
		    "\n");
	} else if (strcmp(what, DM_USE_DUMP) == 0) {
		*usage_string = dgettext(TEXT_DOMAIN,
		    "%s is in use by %s. Please see dumpadm(1M)."
		    "\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(1M).\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(1M)."
		    "\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(1M).\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);
}