/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <meta.h>
#include <sdssc.h>
#include <mdiox.h>
#include <meta_repartition.h>
#include "volume_dlist.h"
#include "volume_error.h"
#include "volume_output.h"
#include "layout_device_util.h"
#include "layout_discovery.h"
#include "layout_dlist_util.h"
#include "layout_request.h"
#include "layout_svm_util.h"
static int _max_hsps = 1000; /* # of HSPs (arbitrary limit) */
static int _max_devs = 8192; /* # of SVM volumes allowed */
static int _max_devs_cfg = 128; /* # of SVM volumes configured */
static int _max_sets = 4; /* # of SVM disk sets */
/* volume name prefixes for generating new names */
static const char *_hsp_prefix = "hsp";
static const char *_dev_prefix = "d";
/*
* dynamically allocated arrays to track used HSP (hspXXX) and volume
* names (dXXX) by number
*/
static boolean_t *hsps_by_number = NULL;
static boolean_t *devs_by_number = NULL;
/*
* This struct remembers a diskset and the names of
* the disks in the set
*/
typedef struct {
char *name;
dlist_t *disknames;
dlist_t *hsps;
} diskset_t;
/*
* list of diskset_t for known disksets
*/
static dlist_t *_disksets = NULL;
static int add_diskset(
char *diskset);
static int add_diskset_diskname(
char *diskset,
char *diskname);
static int add_diskset_hsp(
char *diskset,
char *hspname);
static int add_diskset_hsp_spare(
char *diskset,
char *hspname,
char *spare);
static int is_disk_in_local_diskset(
dm_descriptor_t disk,
boolean_t *bool);
static int is_disk_in_named_diskset(
dm_descriptor_t disk,
char *dsname,
boolean_t *bool);
/* SVM snapshot stuff */
typedef enum {
SVM_DISKSET = 0,
SVM_MDB,
SVM_STRIPE,
SVM_MIRROR,
SVM_RAID,
SVM_TRANS,
SVM_SP,
SVM_HSP,
SVM_HS,
SVM_DRIVE
} svm_type_t;
typedef struct svm_snap_entry {
struct svm_snap_entry *next;
char *diskset;
svm_type_t type;
char *name;
char *slice;
} svm_snap_t;
static svm_snap_t *svm_snapshot(int *errp);
static void free_svm_snapshot(svm_snap_t *listp);
static char *type_name(svm_type_t type);
static int add_record(
svm_snap_t **listp,
char *setname,
svm_type_t type,
char *mname,
char *slice_name);
static int diskset_info(svm_snap_t **listp, mdsetname_t *sp);
static void free_names(mdnamelist_t *nlp);
static int load_svm(svm_snap_t **listp);
static int new_entry(
svm_snap_t **listp,
char *sname,
svm_type_t type,
char *mname,
mdsetname_t *sp);
/*
* FUNCTION: scan_svm_names(char *diskset)
*
* INPUT: diskset - a char * disk set name
*
* PURPOSE: Take a snapshot of the current SVM config.
*
* Scan it and remember:
* 1. all known disk sets
* s. the disks in the named disk set
* 3. the used device and HSP names in the named disk set
* 4. the HSPs in the disk set
* 5. the spares in the HSPs
*/
int
scan_svm_names(
char *diskset)
{
int ndisks = 0;
int nhsps = 0;
int ndevices = 0;
int nsets = 0;
int number = 0;
int error = 0;
svm_snap_t *headp = NULL;
svm_snap_t *listp = NULL;
char *tablefmt = " %-20s %-10s %-20s %-10s\n";
oprintf(OUTPUT_TERSE,
gettext("\nScanning system SVM configuration...\n"));
headp = svm_snapshot(&error);
if (error != 0) {
oprintf(OUTPUT_TERSE,
gettext("failed to scan SVM devices\n"));
return (error);
}
if (error == 0) {
if ((error = get_max_number_of_devices(&_max_devs_cfg)) == 0) {
oprintf(OUTPUT_VERBOSE,
gettext(" configured maximum number of "
"volumes: %d\n"),
_max_devs_cfg);
}
}
if (error == 0) {
if ((error = get_max_number_of_disksets(&_max_sets)) == 0) {
oprintf(OUTPUT_VERBOSE,
gettext(" configured maximum number of "
"disk sets: %d\n"),
_max_sets);
}
}
if (error == 0) {
/* array is realloc'ed as necessary */
if ((hsps_by_number =
(boolean_t *)calloc(_max_hsps, sizeof (boolean_t))) == NULL) {
oprintf(OUTPUT_TERSE,
gettext("failed to allocate HSP name array\n"));
error = ENOMEM;
}
}
if (error == 0) {
/* array is realloc'ed as necessary */
if ((devs_by_number =
(boolean_t *)calloc(_max_devs, sizeof (boolean_t))) == NULL) {
oprintf(OUTPUT_TERSE,
gettext("failed to allocate volume name array\n"));
error = ENOMEM;
}
}
if ((error == 0) && (get_max_verbosity() >= OUTPUT_DEBUG)) {
(void) oprintf(OUTPUT_DEBUG, "\n");
(void) oprintf(OUTPUT_DEBUG,
tablefmt,
gettext("disk set"),
gettext("dev type"),
gettext("name"),
gettext("slice"));
(void) oprintf(OUTPUT_DEBUG,
" -----------------------------------"
"-----------------------------------\n");
}
for (listp = headp; listp != NULL && error == 0; listp = listp->next) {
oprintf(OUTPUT_DEBUG,
tablefmt,
listp->diskset,
type_name(listp->type),
listp->name,
listp->slice);
switch (listp->type) {
case SVM_DISKSET:
error = add_diskset(listp->name);
++nsets;
break;
case SVM_DRIVE:
error = add_diskset_diskname(listp->diskset, listp->name);
/* is this drive in the requested diskset? */
if (string_case_compare(diskset, listp->diskset) == 0) {
++ndisks;
}
break;
case SVM_MIRROR:
case SVM_RAID:
case SVM_TRANS:
case SVM_SP:
case SVM_STRIPE:
/* is this SVM volume in the requested diskset? */
if (string_case_compare(diskset, listp->diskset) == 0) {
/* isolate device name from "poolname/dXXXX" */
char *cp = strrchr(listp->name, '/');
if (cp != NULL) {
++cp;
} else {
cp = listp->name;
}
/* BEGIN CSTYLED */
/*
* names for requested devices and HSPs are remembered
* so that the default name generation scheme knows
* which names are already being used
*/
/* END CSTYLED */
/* extract device number from name "dXXXX" */
if (sscanf(cp, "d%d", &number) != EOF) {
oprintf(OUTPUT_DEBUG,
gettext(" device: %6s number: %3d\n"),
cp, number);
if (number > _max_devs) {
/* hit current limit, expand it */
boolean_t *tmp =
(boolean_t *)realloc((void *)_max_devs,
(number * sizeof (boolean_t)));
if (tmp == NULL) {
error = ENOMEM;
} else {
_max_devs = number;
devs_by_number = tmp;
}
}
if ((error == 0) &&
(devs_by_number[number] == B_FALSE)) {
devs_by_number[number] = B_TRUE;
++ndevices;
}
}
}
break;
case SVM_HSP:
/* is this HSP in the requested diskset? */
if (string_case_compare(diskset, listp->diskset) == 0) {
/* isolate HSP name from "poolname/hspXXX" */
char *cp = strrchr(listp->name, '/');
if (cp != NULL) {
++cp;
} else {
cp = listp->name;
}
/* extract pool number from name "hspXXX" */
if (sscanf(cp, "hsp%03d", &number) != EOF) {
oprintf(OUTPUT_DEBUG,
gettext(" HSP: %6s number: %3d\n"),
cp, number);
if (number > _max_hsps) {
/* hit our arbitrary limit, double it */
boolean_t *tmp =
(boolean_t *)realloc((void *)hsps_by_number,
2 * _max_hsps * sizeof (boolean_t));
if (tmp != NULL) {
_max_hsps *= 2;
hsps_by_number = tmp;
} else {
error = ENOMEM;
}
}
if ((error == 0) &&
(hsps_by_number[number] == B_FALSE)) {
hsps_by_number[number] = B_TRUE;
error = add_diskset_hsp(diskset, cp);
++nhsps;
}
}
}
break;
case SVM_HS:
/* is this hot spare in the requested disk set? */
if (string_case_compare(diskset, listp->diskset) == 0) {
/* isolate HSP name from "poolname/hspXXXX" */
char *cp = strrchr(listp->name, '/');
if (cp != NULL) {
++cp;
} else {
cp = listp->name;
}
error = add_diskset_hsp_spare(diskset, cp, listp->slice);
}
break;
case SVM_MDB:
default:
break;
}
}
free_svm_snapshot(headp);
if (error == 0) {
/* available diskset? subtract 1 for the local set */
if ((diskset_exists(diskset) != B_TRUE) &&
(nsets >= _max_sets)) {
volume_set_error(
gettext("Disk set \"%s\" cannot be created, the "
"maximum number of disk sets (%d) already "
"exists.\n"),
diskset, _max_sets);
error = -1;
}
}
if (error == 0) {
oprintf(OUTPUT_VERBOSE,
gettext("\n Disk set \"%s\" has:\n\n"), diskset);
oprintf(OUTPUT_VERBOSE,
gettext(" %d drives\n"), ndisks);
oprintf(OUTPUT_VERBOSE,
gettext(" %d volumes\n"), ndevices);
oprintf(OUTPUT_VERBOSE,
gettext(" %d HSPs\n"), nhsps);
} else {
free(hsps_by_number);
free(devs_by_number);
hsps_by_number = (boolean_t *)NULL;
devs_by_number = (boolean_t *)NULL;
}
return (error);
}
/*
* FUNCTION: release_svm_names()
*
* PURPOSE: Release snapshot of the current SVM config.
*
* Free memory allocated by scan_svm_names()
*/
void
release_svm_names()
{
dlist_t *iter;
for (iter = _disksets; iter != NULL; iter = iter->next) {
diskset_t *diskset = (diskset_t *)iter->obj;
dlist_free_items(diskset->disknames, free);
dlist_free_items(diskset->hsps, free_devconfig);
free(diskset->name);
}
dlist_free_items(_disksets, free);
_disksets = NULL;
if (hsps_by_number != NULL)
free(hsps_by_number);
if (devs_by_number != NULL)
free(devs_by_number);
hsps_by_number = (boolean_t *)NULL;
devs_by_number = (boolean_t *)NULL;
}
/*
* FUNCTION: diskset_exists(char *diskset)
*
* INPUT: dsname - a char * diskset name
*
* RETURNS: boolean_t - B_TRUE if the named diskset exists
* B_FALSE otherwise
*
* PURPOSE: Checks the list of known disk sets and determines
* if the input name is in that list.
*/
boolean_t
diskset_exists(
char *dsname)
{
dlist_t *iter;
for (iter = _disksets; iter != NULL; iter = iter->next) {
diskset_t *diskset = (diskset_t *)iter->obj;
if (string_case_compare(dsname, diskset->name) == 0) {
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* FUNCTION: add_diskset(char *dsname)
*
* INPUT: dsname - a char * disk set name
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Add the named disk set to the list of known disk sets.
*/
static int
add_diskset(
char *dsname)
{
dlist_t *iter;
int error = 0;
for (iter = _disksets; iter != NULL; iter = iter->next) {
diskset_t *diskset = (diskset_t *)iter->obj;
if (string_case_compare(diskset->name, dsname) == 0) {
break;
}
}
if (iter == NULL) {
dlist_t *item = NULL;
diskset_t *diskset = (diskset_t *)calloc(1, sizeof (diskset_t));
if (diskset == NULL) {
error = ENOMEM;
} else {
diskset->hsps = NULL;
diskset->name = strdup(dsname);
if (diskset->name == NULL) {
free(diskset);
error = ENOMEM;
} else {
if ((item = dlist_new_item(diskset)) == NULL) {
free(diskset->name);
free(diskset);
error = ENOMEM;
} else {
_disksets = dlist_append(item, _disksets, AT_HEAD);
oprintf(OUTPUT_DEBUG,
gettext(" added disk set %s \n"), dsname);
}
}
}
}
return (error);
}
/*
* FUNCTION: add_diskset_diskname(char *diskset, char *diskname)
*
* INPUT: dsname - a char * disk set name
* diskname - a char * disk name
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Add the disk name to the named disk set's list of disks.
*
* The input diskname is fully qualified with the path
* to the raw disk device (/dev/rdsk/cXtXdXsX) which is
* not relevant, so it is removed.
*/
static int
add_diskset_diskname(
char *dsname,
char *diskname)
{
dlist_t *iter;
int error = 0;
for (iter = _disksets; iter != NULL; iter = iter->next) {
diskset_t *diskset = (diskset_t *)iter->obj;
if (string_case_compare(diskset->name, dsname) == 0) {
dlist_t *item = NULL;
char *name = NULL;
char *cp = NULL;
/* trim leading path */
if ((cp = strrchr(diskname, '/')) != 0) {
if ((name = strdup(cp+1)) == NULL) {
error = ENOMEM;
}
} else if ((name = strdup(diskname)) == NULL) {
error = ENOMEM;
}
if ((item = dlist_new_item(name)) == NULL) {
free(name);
error = ENOMEM;
} else {
diskset->disknames =
dlist_append(item, diskset->disknames, AT_HEAD);
}
break;
}
}
if ((error == 0) && (iter == NULL)) {
/* new disk set */
if ((error = add_diskset(dsname)) == 0) {
return (add_diskset_diskname(dsname, diskname));
}
}
return (error);
}
/*
* FUNCTION: add_diskset_hsp(char *dsname, char *hspname)
*
* INPUT: dsname - a char * disk set name
* hspname - a char * HSP name
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Model a new HSP for the named disk set.
*
* Metassist can use existing HSPs to service new volumes.
*
* It is necessary to have a model of what HSPs currently
* exist for each disk set.
*
* This function takes information found during discovery
* and turns it into a form usable by the HSP layout code.
*/
static int
add_diskset_hsp(
char *dsname,
char *hspname)
{
dlist_t *iter;
int error = 0;
for (iter = _disksets; iter != NULL; iter = iter->next) {
diskset_t *diskset = (diskset_t *)iter->obj;
if (string_case_compare(diskset->name, dsname) == 0) {
dlist_t *item = NULL;
devconfig_t *hsp = NULL;
if (((error = new_devconfig(&hsp, TYPE_HSP)) != 0) ||
(error = devconfig_set_name(hsp, hspname))) {
free_devconfig(hsp);
} else {
if ((item = dlist_new_item(hsp)) == NULL) {
free_devconfig(hsp);
error = ENOMEM;
} else {
diskset->hsps =
dlist_append(item, diskset->hsps, AT_TAIL);
oprintf(OUTPUT_DEBUG,
gettext(" added %s to disk set %s\n"),
hspname, dsname);
}
}
break;
}
}
if ((error == 0) && (iter == NULL)) {
if ((error = add_diskset(dsname)) == 0) {
return (add_diskset_hsp(dsname, hspname));
}
}
return (error);
}
/*
* FUNCTION: add_diskset_hsp_spare(char *dsname, char *hspname,
* char *sparename)
*
* INPUT: dsname - a char * diskset name
* hspname - a char * HSP name
* sparename - a char * hot spare (slice) name
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Locate the named hot spare pool in the named disk set and
* add the named spare slice to its list of spares.
*
* Metassist can use existing HSPs to service new volumes.
*
* It is necessary to have a model of what HSPs currently
* exist for each disk set.
*
* This function takes information found during discovery
* and turns it into a form usable by the HSP layout code.
*/
static int
add_diskset_hsp_spare(
char *dsname,
char *hspname,
char *sparename)
{
dlist_t *iter;
int error = 0;
for (iter = _disksets; iter != NULL; iter = iter->next) {
diskset_t *diskset = (diskset_t *)iter->obj;
if (string_case_compare(diskset->name, dsname) == 0) {
dlist_t *item =
dlist_find(
diskset->hsps, hspname,
compare_string_to_devconfig_name);
if (item != NULL) {
/* add spare to HSP */
devconfig_t *hsp = (devconfig_t *)item->obj;
dm_descriptor_t slice = (dm_descriptor_t)0;
(void) slice_get_by_name(sparename, &slice);
if (slice == (dm_descriptor_t)0) {
oprintf(OUTPUT_TERSE,
gettext("warning: ignoring nonexistent "
"slice %s defined in %s\n"),
sparename, hspname);
} else {
uint64_t nbytes = 0;
uint32_t index = 0;
devconfig_t *spare = NULL;
/* build a devconfig_t model of the slice */
if (((error = slice_get_size(slice, &nbytes)) != 0) ||
(error = slice_get_index(slice, &index)) ||
(error = new_devconfig(&spare, TYPE_SLICE)) ||
(error = devconfig_set_name(spare, sparename)) ||
(error = devconfig_set_size(spare, nbytes)) ||
(error = devconfig_set_slice_index(spare, index))) {
free_devconfig(spare);
} else {
if ((item = dlist_new_item(spare)) == NULL) {
error = ENOMEM;
free_devconfig(spare);
} else {
dlist_t *spares;
spares = devconfig_get_components(hsp);
spares = dlist_append(item, spares, AT_TAIL);
devconfig_set_components(hsp, spares);
oprintf(OUTPUT_DEBUG,
gettext(" added %s to %s in "
"disk set %s\n"),
sparename, hspname, dsname);
}
}
}
break;
} else {
if ((error = add_diskset_hsp(dsname, hspname)) == 0) {
return (add_diskset_hsp_spare(
dsname, hspname, sparename));
}
}
}
}
return (error);
}
/*
* Return a list of disks in the given diskset.
*
* @param dsname
* The name of the named disk set, or "" for the local
* set.
*
* @param disks
* RETURN: pointer to the list of disks in the given disk
* set
*
* @return 0 if succesful, non-zero otherwise
*/
int
get_disks_in_diskset(
char *dsname,
dlist_t **disks)
{
dlist_t *known_disks;
int error = 0;
*disks = NULL;
if ((error = get_known_disks(&known_disks)) == 0) {
dlist_t *iter;
/* For each known disk... */
for (iter = known_disks;
iter != NULL && error == 0;
iter = iter->next) {
dm_descriptor_t disk = (uintptr_t)iter->obj;
boolean_t in_diskset = B_FALSE;
/* If this disk is in the given set... */
error = is_disk_in_diskset(disk, dsname, &in_diskset);
if (error == 0 && in_diskset == B_TRUE) {
dlist_t *item = dlist_new_item((void *)(uintptr_t)disk);
*disks = dlist_append(item, *disks, AT_TAIL);
}
}
}
return (error);
}
/*
* FUNCTION: is_disk_in_diskset(dm_descriptor_t disk, char *dsname,
* boolean_t *bool)
*
* INPUT: disk - dm_descriptor_t disk handle
* dsname - char * diskset name, or MD_LOCAL_NAME for
* the local set.
*
* OUTPUT: bool - pointer to a boolean_t to hold the result
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Determine if the input disk is known to be in the
* given diskset.
*/
int
is_disk_in_diskset(
dm_descriptor_t disk,
char *dsname,
boolean_t *bool)
{
if (string_case_compare(dsname, MD_LOCAL_NAME) == 0) {
return (is_disk_in_local_diskset(disk, bool));
}
return (is_disk_in_named_diskset(disk, dsname, bool));
}
static int
is_disk_in_local_diskset(
dm_descriptor_t disk,
boolean_t *bool)
{
dlist_t *iter;
dlist_t *aliases = NULL;
boolean_t in_named_diskset = B_FALSE;
char *name = NULL;
int error = 0;
*bool = B_FALSE;
error = get_display_name(disk, &name);
if (error == 0) {
error = get_aliases(disk, &aliases);
if (error == 0) {
/* For each known disk set... */
for (iter = _disksets;
iter != NULL && in_named_diskset == B_FALSE;
iter = iter->next) {
diskset_t *diskset = (diskset_t *)iter->obj;
dlist_t *names = diskset->disknames;
/* Check disk name */
in_named_diskset = dlist_contains(
names, name, compare_device_names);
/* Check disk aliases */
if (in_named_diskset == B_FALSE) {
dlist_t *iter2;
for (iter2 = aliases;
iter2 != NULL && in_named_diskset == B_FALSE;
iter2 = iter2->next) {
in_named_diskset = dlist_contains(names,
(char *)iter2->obj, compare_device_names);
}
}
}
}
}
if (error == 0) {
*bool = (in_named_diskset == B_TRUE ? B_FALSE : B_TRUE);
}
return (error);
}
static int
is_disk_in_named_diskset(
dm_descriptor_t disk,
char *dsname,
boolean_t *bool)
{
dlist_t *iter;
int error = 0;
boolean_t in_diskset = B_FALSE;
*bool = B_FALSE;
for (iter = _disksets;
(iter != NULL) && (in_diskset == B_FALSE);
iter = iter->next) {
diskset_t *diskset = (diskset_t *)iter->obj;
if (string_case_compare(diskset->name, dsname) == 0) {
dlist_t *names = diskset->disknames;
dlist_t *aliases = NULL;
char *name = NULL;
((error = get_display_name(disk, &name)) != 0) ||
(error = get_aliases(disk, &aliases));
if (error != 0) {
break;
}
/* check disk name */
in_diskset = dlist_contains(names, name, compare_device_names);
/* check disk aliases */
if (in_diskset == B_FALSE) {
dlist_t *iter2;
for (iter2 = aliases;
(iter2 != NULL) && (in_diskset == B_FALSE);
iter2 = iter2->next) {
in_diskset = dlist_contains(names,
(char *)iter2->obj, compare_device_names);
}
}
}
}
*bool = in_diskset;
return (error);
}
/*
* FUNCTION: is_disk_in_other_diskset(dm_descriptor_t disk, char *dsname,
* boolean_t *bool)
*
* INPUT: disk - dm_descriptor_t disk handle
* dsname - char * disk set name
*
* OUTPUT: bool - pointer to a boolean_t to hold the result.
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Determine if the named disk is known to be in a disk set
* other than the named disk set.
*/
int
is_disk_in_other_diskset(
dm_descriptor_t disk,
char *dsname,
boolean_t *bool)
{
boolean_t in_other = B_FALSE;
dlist_t *iter;
dlist_t *aliases = NULL;
char *name = NULL;
char *cp = NULL;
int error = 0;
((error = get_display_name(disk, &name)) != 0) ||
(error = get_aliases(disk, &aliases));
if (error != 0) {
return (error);
}
/*
* discard the leading path, it is probably /dev/dsk
* and the disk set disk names are all /dev/rdsk/...
*
* aliases do not have leading paths
*/
cp = strrchr(name, '/');
if (cp != NULL) {
++cp;
} else {
cp = name;
}
name = cp;
for (iter = _disksets;
(iter != NULL) && (in_other == B_FALSE);
iter = iter->next) {
diskset_t *diskset = (diskset_t *)iter->obj;
dlist_t *names = diskset->disknames;
if (string_case_compare(diskset->name, dsname) == 0) {
/* skip named disk set */
continue;
}
/* see if disk's name is in disk set's name list */
in_other = dlist_contains(names, name, compare_device_names);
/* see if any of the disk's aliases is in name list */
if (in_other == B_FALSE) {
dlist_t *iter2;
for (iter2 = aliases;
(iter2 != NULL) && (in_other == B_FALSE);
iter2 = iter2->next) {
in_other = dlist_contains(names,
(char *)iter2->obj, compare_device_names);
}
}
}
*bool = in_other;
return (error);
}
/*
* FUNCTION: hsp_get_default_for_diskset(char *diskset,
* devconfig_t **hsp)
*
* INPUT: diskset - char * disk set name
*
* RETURNS: devconfig_t * - pointer to the first HSP in the disk set
* NULL if none found
*
* PURPOSE: Locate the first HSP in the named disk set.
*/
int
hsp_get_default_for_diskset(
char *diskset,
devconfig_t **hsp)
{
dlist_t *iter = _disksets;
*hsp = NULL;
for (; (iter != NULL) && (*hsp == NULL); iter = iter->next) {
diskset_t *set = (diskset_t *)iter->obj;
if (string_case_compare(set->name, diskset) == 0) {
dlist_t *item = set->hsps;
if (item != NULL) {
*hsp = item->obj;
}
}
}
return (0);
}
/*
* FUNCTION: get_n_metadb_replicas(int *nreplicas)
*
* OUTPUT: nreplicas - pointer to int to hold the result
*
* RETURNS: int - 0 on success
* !0 on failure
*
* PURPOSE: Check the number of replicas configured for the local set.
*/
int
get_n_metadb_replicas(
int *nreplicas)
{
mdsetname_t *sp;
md_replicalist_t *rlp = NULL;
md_error_t mderror = mdnullerror;
int error = 0;
*nreplicas = 0;
sp = metasetname(MD_LOCAL_NAME, &mderror);
if (!mdisok(&mderror)) {
volume_set_error(mde_sperror(&mderror, NULL));
mdclrerror(&mderror);
error = -1;
} else {
*nreplicas = metareplicalist(sp, MD_BASICNAME_OK, &rlp, &mderror);
if (!mdisok(&mderror)) {
volume_set_error(mde_sperror(&mderror, NULL));
mdclrerror(&mderror);
error = -1;
} else if (rlp != NULL) {
metafreereplicalist(rlp);
rlp = NULL;
}
if (*nreplicas < 0) {
*nreplicas = 0;
}
}
return (error);
}
/*
* FUNCTION: hsp_get_by_name(char *diskset, char *name,
* devconfig_t **hsp)
*
* INPUT: diskset - char * disk set name
* name - char * HSP name
*
* OUTPUT: hsp - a devconfig_t * - pointer to hold
* the named HSP if none found
*
* PURPOSE: Locate the named HSP in the named disk set.
*/
int
hsp_get_by_name(
char *diskset,
char *name,
devconfig_t **hsp)
{
dlist_t *iter = _disksets;
*hsp = NULL;
for (; (iter != NULL) && (*hsp == NULL); iter = iter->next) {
diskset_t *set = (diskset_t *)iter->obj;
if (string_case_compare(set->name, diskset) == 0) {
dlist_t *item = dlist_find(
set->hsps, name, compare_string_to_devconfig_name);
if (item != NULL) {
*hsp = item->obj;
}
}
}
return (0);
}
/*
* FUNCTION: is_volume_name_valid(char *name)
*
* OUTPUT: name - pointer to a char * volume name
*
* RETURNS: boolean_t - B_TRUE if the input name is valid
* B_FALSE otherwise
*
* PURPOSE: Wrapper around libmeta volume name validation method.
*/
boolean_t
is_volume_name_valid(
char *name)
{
return (is_metaname(name));
}
/*
* FUNCTION: is_hsp_name_valid(char *name)
*
* INPUT: name - char * HSP name
*
* RETURNS: boolean_t - B_TRUE if the input name is valid
* B_FALSE otherwise
*
* PURPOSE: Wrapper around libmeta HSP name validation method.
*/
boolean_t
is_hsp_name_valid(
char *name)
{
return (is_hspname(name));
}
/*
* FUNCTION: extract_index(char *name, char *prefix, char *num_fmt,
* int *index)
*
* INPUT: name - const char * volume name
* prefix - const char * fixed part of format string
* num_fmt - const char * format of number to extract (e.g. %d)
*
* OUTPUT: index - pointer to int to hold numeric part of name
*
* RETURNS: boolean_t - B_TRUE if the input name is parsed correctly
* B_FALSE otherwise
*
* PURPOSE: Extract the numeric portion of a device name for use
* by higher-level functions.
*/
static boolean_t
extract_index(
const char *name,
const char *prefix,
const char *num_fmt,
int *index)
{
char buf[MAXNAMELEN];
const char *cp;
const char *fmt = buf;
if ((cp = strrchr(name, '/')) != NULL) {
++cp;
} else {
cp = name;
}
(void) snprintf(buf, sizeof (buf), "%s%s", prefix, num_fmt);
if (sscanf(cp, fmt, index) == 1)
return (B_TRUE);
else
return (B_FALSE);
}
/*
* FUNCTION: is_volume_name_in_range(char *name)
*
* INPUT: name - char * volume name
*
* RETURNS: boolean_t - B_TRUE if the input name is in the allowed
* range of names
* B_FALSE otherwise
*
* PURPOSE: Determine if the input volume name is within the allowed
* range of device names (0 <= n < max # of devices configured).
*/
boolean_t
is_volume_name_in_range(
char *name)
{
int index = -1;
if (extract_index(name, _dev_prefix, "%d", &index)) {
if (index >= 0 && index < _max_devs_cfg) {
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* FUNCTION: reserve_volume_name(char *name)
*
* INPUT: name - a char * volume name
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Mark a volume name/number as used.
*
* Assumes that the input name has been validated.
*
* if the name is not currently available, return -1
*/
int
reserve_volume_name(
char *name)
{
int index = -1;
if (extract_index(name, _dev_prefix, "%d", &index)) {
if (devs_by_number[index] != B_TRUE) {
devs_by_number[index] = B_TRUE;
return (0);
}
}
return (-1);
}
/*
* FUNCTION: reserve_hsp_name(char *name)
*
* INPUT: name - a char * hsp name
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Mark a HSP name/number as used.
*
* Assumes that the input name has been validated.
*
* if the name is not currently available, return -1
*/
int
reserve_hsp_name(
char *name)
{
int index = -1;
if (extract_index(name, _hsp_prefix, "%03d", &index)) {
if (hsps_by_number[index] != B_TRUE) {
hsps_by_number[index] = B_TRUE;
return (0);
}
}
return (-1);
}
/*
* FUNCTION: release_volume_name(char *name)
*
* INPUT: name - a char * volume name
*
* PURPOSE: release the input volume name.
*
* Extract volume number from the input name
* and use it to index into the array of used
* volume numbers. Make that volume number
* available for use again.
*/
void
release_volume_name(
char *name)
{
int index = -1;
if (name != NULL && extract_index(name, _dev_prefix, "%d", &index)) {
oprintf(OUTPUT_DEBUG,
gettext("released volume name %s%d\n"),
_dev_prefix, index);
devs_by_number[index] = B_FALSE;
}
}
/*
* FUNCTION: release_hsp_name(char *name)
*
* INPUT: name - a char * HSP name
*
* PURPOSE: release the input HSP name.
*
* Extract volume number from the input name
* and use it to index into the array of used
* hsp numbers. Make that hsp number available
* for use again.
*/
void
release_hsp_name(
char *name)
{
int index = -1;
if (name != NULL && extract_index(name, _hsp_prefix, "%d", &index)) {
oprintf(OUTPUT_DEBUG,
gettext("released hsp name %s%d\n"),
_hsp_prefix, index);
hsps_by_number[index] = B_FALSE;
}
}
/*
* FUNCTION: get_next_volume_name(char **name)
*
* OUTPUT: name - pointer to a char * to hold volume name
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: generate a new volume name using the standard device
* name prefix and the lowest available device number.
*
* if type == MIRROR, determine the next available mirror
* name according to the convention that a mirror name is
* a multiple of 10.
*
* If such a name is unavailable, use the next available name.
*/
int
get_next_volume_name(
char **name,
component_type_t type)
{
int next = 0;
for (next = 0; next < _max_devs_cfg; ++next) {
if ((type == TYPE_MIRROR && ((next % 10) != 0)) ||
(type != TYPE_MIRROR && ((next % 10) == 0))) {
/* use/save multiples of 10 for mirrors */
continue;
}
if (devs_by_number[next] != B_TRUE) {
break;
}
}
if ((next == _max_devs_cfg) && (type == TYPE_MIRROR)) {
/* try next sequentially available name */
for (next = 0; next < _max_devs_cfg; ++next) {
if (devs_by_number[next] != B_TRUE) {
break;
}
}
}
if (next == _max_devs_cfg) {
volume_set_error(
gettext("ran out of logical volume names.\n"));
return (-1);
}
*name = (char *)calloc(MAXNAMELEN, sizeof (char));
if (*name == NULL) {
return (ENOMEM);
}
(void) snprintf(*name, MAXNAMELEN-1, "%s%d", _dev_prefix, next);
devs_by_number[next] = B_TRUE;
return (0);
}
/*
* FUNCTION: get_next_submirror_name(char *mname, char **subname)
*
* INPUT: mname - pointer to a char * mirror name
* OUTPUT: subname - pointer to a char * to hold submirror name
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Determine the next available submirror name according
* to the convention that each submirror name is a sequential
* increment of its mirror's name.
*
* If such a name is unavailable, return the next sequentially
* available volume name.
*/
int
get_next_submirror_name(
char *mname,
char **subname)
{
char buf[MAXNAMELEN];
int error = 0;
int next = 0;
int i = 0;
*subname = NULL;
/* try next sequential name: mirror + 1... */
if (extract_index(mname, _dev_prefix, "%d", &next)) {
for (i = next + 1; i < _max_devs_cfg; i++) {
if ((i % 10) == 0) {
/* save for mirrors */
continue;
}
if (devs_by_number[i] == B_FALSE) {
(void) snprintf(buf, MAXNAMELEN-1, "%s%d", _dev_prefix, i);
if ((*subname = strdup(buf)) != NULL) {
devs_by_number[i] = B_TRUE;
} else {
error = ENOMEM;
}
break;
}
}
}
if ((error == 0) && (*subname == NULL)) {
/* name adhering to convention isn't available, */
/* use next sequentially available name */
error = get_next_volume_name(subname, TYPE_STRIPE);
}
return (error);
}
/*
* FUNCTION: get_next_hsp_name(char **name)
*
* OUTPUT: name - pointer to a char * to hold name
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Helper which generates a new hotsparepool name
* using the standard name prefix and the lowest
* available hsp number.
*/
int
get_next_hsp_name(
char **name)
{
int next = 0;
for (next = 0; next < _max_hsps; ++next) {
if (hsps_by_number[next] != B_TRUE) {
break;
}
}
if (next == _max_hsps) {
volume_set_error(gettext("ran out of HSP names"));
return (-1);
}
*name = (char *)calloc(MAXNAMELEN, sizeof (char));
if (*name == NULL) {
oprintf(OUTPUT_TERSE,
gettext("failed to allocate volume name string, "
"out of memory"));
return (ENOMEM);
}
(void) snprintf(*name, MAXNAMELEN-1, "%s%03d", _hsp_prefix, next);
hsps_by_number[next] = B_TRUE;
return (0);
}
static char *
type_name(
svm_type_t type)
{
switch (type) {
case SVM_DISKSET: return (gettext("disk set"));
case SVM_MDB: return (gettext("metadb"));
case SVM_STRIPE: return (gettext("stripe"));
case SVM_MIRROR: return (gettext("mirror"));
case SVM_RAID: return (gettext("raid"));
case SVM_TRANS: return (gettext("trans"));
case SVM_SP: return (gettext("soft partition"));
case SVM_HSP: return (gettext("hot spare pool"));
case SVM_HS: return (gettext("hot spare"));
case SVM_DRIVE: return (gettext("drive"));
default: return (gettext("unknown"));
}
}
static svm_snap_t *
svm_snapshot(int *errp)
{
svm_snap_t *svm_listp = NULL;
*errp = 0;
/* initialize the cluster library entry points */
if (sdssc_bind_library() == SDSSC_ERROR) {
volume_set_error(gettext("sdssc_bin_library() failed\n"));
*errp = -1;
} else {
/* load the SVM cache */
*errp = load_svm(&svm_listp);
if (*errp != 0) {
free_svm_snapshot(svm_listp);
svm_listp = NULL;
}
}
return (svm_listp);
}
static void
free_svm_snapshot(svm_snap_t *listp) {
svm_snap_t *nextp;
while (listp != NULL) {
nextp = listp->next;
free((void *)listp->diskset);
free((void *)listp->name);
free((void *)listp->slice);
free((void *)listp);
listp = nextp;
}
}
static int
add_record(
svm_snap_t **listp,
char *setname,
svm_type_t type,
char *mname,
char *slice_name)
{
svm_snap_t *sp;
sp = (svm_snap_t *)malloc(sizeof (svm_snap_t));
if (sp == NULL) {
return (ENOMEM);
}
if ((sp->diskset = strdup(setname)) == NULL) {
free(sp);
return (ENOMEM);
}
if ((sp->name = strdup(mname)) == NULL) {
free(sp->diskset);
free(sp);
return (ENOMEM);
}
sp->type = type;
if ((sp->slice = strdup(slice_name)) == NULL) {
free(sp->diskset);
free(sp->name);
free(sp);
return (ENOMEM);
}
sp->next = *listp;
*listp = sp;
return (0);
}
static int
diskset_info(
svm_snap_t **listp,
mdsetname_t *sp)
{
md_error_t error = mdnullerror;
md_replicalist_t *replica_list = NULL;
md_replicalist_t *mdbp;
mdnamelist_t *nlp;
mdnamelist_t *trans_list = NULL;
mdnamelist_t *mirror_list = NULL;
mdnamelist_t *raid_list = NULL;
mdnamelist_t *stripe_list = NULL;
mdnamelist_t *sp_list = NULL;
mdhspnamelist_t *hsp_list = NULL;
if (metareplicalist(sp, MD_BASICNAME_OK, &replica_list, &error) < 0) {
/* there are no metadb's; that is ok, no need to check the rest */
mdclrerror(&error);
return (0);
}
mdclrerror(&error);
for (mdbp = replica_list; mdbp != NULL; mdbp = mdbp->rl_next) {
char size[MAXPATHLEN];
(void) snprintf(size, sizeof (size), "%d",
(int)mdbp->rl_repp->r_nblk);
if (new_entry(listp, mdbp->rl_repp->r_namep->cname, SVM_MDB, size,
sp)) {
metafreereplicalist(replica_list);
return (ENOMEM);
}
}
metafreereplicalist(replica_list);
if (meta_get_trans_names(sp, &trans_list, 0, &error) >= 0) {
for (nlp = trans_list; nlp != NULL; nlp = nlp->next) {
if (new_entry(listp, nlp->namep->cname, SVM_TRANS,
nlp->namep->cname, sp)) {
free_names(trans_list);
return (ENOMEM);
}
}
free_names(trans_list);
}
mdclrerror(&error);
if (meta_get_mirror_names(sp, &mirror_list, 0, &error) >= 0) {
for (nlp = mirror_list; nlp != NULL; nlp = nlp->next) {
if (add_record(listp, sp->setname, SVM_MIRROR,
nlp->namep->cname, "")) {
free_names(mirror_list);
return (ENOMEM);
}
}
free_names(mirror_list);
}
mdclrerror(&error);
if (meta_get_raid_names(sp, &raid_list, 0, &error) >= 0) {
for (nlp = raid_list; nlp != NULL; nlp = nlp->next) {
mdname_t *mdn;
md_raid_t *raid;
mdn = metaname(&sp, nlp->namep->cname, META_DEVICE, &error);
mdclrerror(&error);
if (mdn == NULL) {
continue;
}
raid = meta_get_raid(sp, mdn, &error);
mdclrerror(&error);
if (raid != NULL) {
int i;
for (i = 0; i < raid->cols.cols_len; i++) {
if (new_entry(listp,
raid->cols.cols_val[i].colnamep->cname, SVM_RAID,
nlp->namep->cname, sp)) {
free_names(raid_list);
return (ENOMEM);
}
}
}
}
free_names(raid_list);
}
mdclrerror(&error);
if (meta_get_stripe_names(sp, &stripe_list, 0, &error) >= 0) {
for (nlp = stripe_list; nlp != NULL; nlp = nlp->next) {
mdname_t *mdn;
md_stripe_t *stripe;
mdn = metaname(&sp, nlp->namep->cname, META_DEVICE, &error);
mdclrerror(&error);
if (mdn == NULL) {
continue;
}
stripe = meta_get_stripe(sp, mdn, &error);
mdclrerror(&error);
if (stripe != NULL) {
int i;
for (i = 0; i < stripe->rows.rows_len; i++) {
md_row_t *rowp;
int j;
rowp = &stripe->rows.rows_val[i];
for (j = 0; j < rowp->comps.comps_len; j++) {
md_comp_t *component;
component = &rowp->comps.comps_val[j];
if (new_entry(listp, component->compnamep->cname,
SVM_STRIPE, nlp->namep->cname, sp)) {
free_names(stripe_list);
return (ENOMEM);
}
}
}
}
}
free_names(stripe_list);
}
mdclrerror(&error);
if (meta_get_sp_names(sp, &sp_list, 0, &error) >= 0) {
for (nlp = sp_list; nlp != NULL; nlp = nlp->next) {
mdname_t *mdn;
md_sp_t *soft_part;
mdn = metaname(&sp, nlp->namep->cname, META_DEVICE, &error);
mdclrerror(&error);
if (mdn == NULL) {
continue;
}
soft_part = meta_get_sp(sp, mdn, &error);
mdclrerror(&error);
if (soft_part != NULL) {
if (new_entry(listp, soft_part->compnamep->cname, SVM_SP,
nlp->namep->cname, sp)) {
free_names(sp_list);
return (ENOMEM);
}
}
}
free_names(sp_list);
}
mdclrerror(&error);
if (meta_get_hsp_names(sp, &hsp_list, 0, &error) >= 0) {
mdhspnamelist_t *nlp;
for (nlp = hsp_list; nlp != NULL; nlp = nlp->next) {
md_hsp_t *hsp;
hsp = meta_get_hsp(sp, nlp->hspnamep, &error);
mdclrerror(&error);
if (hsp != NULL) {
int i;
for (i = 0; i < hsp->hotspares.hotspares_len; i++) {
md_hs_t *hs;
hs = &hsp->hotspares.hotspares_val[i];
if (add_record(listp, sp->setname, SVM_HS,
nlp->hspnamep->hspname, hs->hsnamep->bname)) {
metafreehspnamelist(hsp_list);
return (ENOMEM);
}
}
}
if (add_record(listp, sp->setname, SVM_HSP,
nlp->hspnamep->hspname, "")) {
metafreehspnamelist(hsp_list);
return (ENOMEM);
}
}
metafreehspnamelist(hsp_list);
}
mdclrerror(&error);
return (0);
}
static void
free_names(
mdnamelist_t *nlp)
{
mdnamelist_t *p;
for (p = nlp; p != NULL; p = p->next) {
meta_invalidate_name(p->namep);
}
metafreenamelist(nlp);
}
/*
* Create a list of SVM devices
*/
static int
load_svm(
svm_snap_t **listp)
{
int max_sets;
md_error_t error = mdnullerror;
int i;
if ((max_sets = get_max_sets(&error)) == 0) {
return (0);
}
if (!mdisok(&error)) {
volume_set_error(
gettext("failed to get maximum number of disk sets.\n"));
mdclrerror(&error);
return (-1);
}
/* for each possible set number, see if we really have a disk set */
for (i = 0; i < max_sets; i++) {
mdsetname_t *sp;
if ((sp = metasetnosetname(i, &error)) == NULL) {
if (!mdisok(&error) && error.info.errclass == MDEC_RPC) {
/* rpc error - no metasets */
break;
}
mdclrerror(&error);
continue;
}
mdclrerror(&error);
if (add_record(listp, sp->setname, SVM_DISKSET, sp->setname, "")) {
metaflushsetname(sp);
return (ENOMEM);
}
/* check for drives in disk sets */
if (sp->setno != 0) {
md_drive_desc *dd;
dd = metaget_drivedesc(sp, MD_BASICNAME_OK | PRINT_FAST,
&error);
mdclrerror(&error);
for (; dd != NULL; dd = dd->dd_next) {
if (add_record(listp, sp->setname, SVM_DRIVE,
dd->dd_dnp->rname, "")) {
metaflushsetname(sp);
return (ENOMEM);
}
}
}
if (diskset_info(listp, sp)) {
metaflushsetname(sp);
return (ENOMEM);
}
metaflushsetname(sp);
}
mdclrerror(&error);
return (0);
}
/* determine if 'sp' is built on a slice */
static int
new_entry(
svm_snap_t **listp,
char *slice_name,
svm_type_t type,
char *mname,
mdsetname_t *sp)
{
mdname_t *mdn;
md_error_t error = mdnullerror;
meta_device_type_t uname_type = UNKNOWN;
/* Determine the appropriate uname type for metaname */
if (type == SVM_MDB || type == SVM_DRIVE || type == SVM_TRANS)
uname_type = LOGICAL_DEVICE;
mdn = metaname(&sp, slice_name, uname_type, &error);
if (!mdisok(&error)) {
mdn = NULL;
}
mdclrerror(&error);
if (mdn != NULL && (
mdn->drivenamep->type == MDT_ACCES ||
mdn->drivenamep->type == MDT_COMP ||
mdn->drivenamep->type == MDT_FAST_COMP)) {
return (add_record(listp, sp->setname, type, mname, mdn->bname));
} else {
return (add_record(listp, sp->setname, type, mname, ""));
}
}
/*
* FUNCTION: get_default_stripe_interlace()
*
* RETURNS: uint64_t - default stripe interlace value
*
* PURPOSE: Helper which retrieves the default stripe interlace
* from libmeta.
*/
uint64_t
get_default_stripe_interlace()
{
/* convert back to bytes */
return ((uint64_t)meta_default_stripe_interlace() * DEV_BSIZE);
}
/*
* FUNCTION: get_max_number_of_devices(int *max)
*
* OUTPUT: max - pointer to int to hold the configured maximum number
* of SVM devices
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Helper which determines the maximum number of allowed
* SVM devices configured for the system.
*
* Wrapper around libmeta function meta_get_max_nunits().
*/
int
get_max_number_of_devices(
int *max)
{
md_error_t mderror = mdnullerror;
*max = meta_get_nunits(&mderror);
if (!mdisok(&mderror)) {
volume_set_error(mde_sperror(&mderror, NULL));
mdclrerror(&mderror);
return (-1);
}
return (0);
}
/*
* FUNCTION: get_max_number_of_disksets(int *max)
*
* OUTPUT: max - pointer to in to hold the configured maximum number
* of disk sets
*
* RETURNS: int - 0 on success
* !0 otherwise
*
* PURPOSE: Helper which determines the maximum number of allowed
* disk sets which has been configured for the system.
*
* Wrapper around libmeta function get_max_sets().
*/
int
get_max_number_of_disksets(
int *max)
{
md_error_t mderror = mdnullerror;
*max = get_max_sets(&mderror);
if (!mdisok(&mderror)) {
volume_set_error(mde_sperror(&mderror, NULL));
mdclrerror(&mderror);
return (-1);
}
return (0);
}
/*
* FUNCTION: is_reserved_replica_slice_index(char *diskset, char *dname,
* uint32_t index, boolean_t *bool)
*
* INPUT: diskset - char * disk set name
* dname - char * disk name
* index - integer index of interest
*
* OUTPUT: bool - pointer to a boolean_t to hold the result
*
* RETURNS: int - 0 - success
* !0 - failure
*
* PURPOSE: Helper which determines if the input slice index on
* the named disk in the named disk set is the replica
* slice that is reserved on disks in disk sets.
*
* The named disk is assumed to be in the named disk set.
*
* Determines if metassist is being run in a simulated
* hardware enironment, if not the libmeta function to
* determine the replica slice index is called.
*
* If simulation is active, then a local implementation
* is used to determine the replica slice index.
*/
int
is_reserved_replica_slice_index(
char *diskset,
char *dname,
uint32_t index,
boolean_t *bool)
{
int error = 0;
boolean_t sim = B_FALSE;
static char *simfile = "METASSISTSIMFILE";
sim = ((getenv(simfile) != NULL) && (strlen(getenv(simfile)) > 0));
if (sim != B_TRUE) {
/* sim disabled: use meta_replicaslice() */
md_error_t mderror = mdnullerror;
mdsetname_t *sp;
mddrivename_t *dnp;
uint_t replicaslice;
/* slice assumed to be on disk in the named disk set */
sp = metasetname(diskset, &mderror);
if (!mdisok(&mderror)) {
volume_set_error(mde_sperror(&mderror, NULL));
mdclrerror(&mderror);
return (-1);
}
dnp = metadrivename(&sp, dname, &mderror);
if (!mdisok(&mderror)) {
volume_set_error(mde_sperror(&mderror, NULL));
mdclrerror(&mderror);
return (-1);
}
if (meta_replicaslice(dnp, &replicaslice, &mderror) != 0) {
volume_set_error(mde_sperror(&mderror, NULL));
mdclrerror(&mderror);
return (-1);
}
*bool = (replicaslice == (uint_t)index);
} else {
dm_descriptor_t disk;
boolean_t efi = B_FALSE;
/* sim enabled: use same logic as meta_replicaslice() */
((error = disk_get_by_name(dname, &disk)) != 0) ||
(error = disk_get_is_efi(disk, &efi));
if (error == 0) {
if (efi == B_FALSE) {
*bool = (index == MD_SLICE7);
} else {
*bool = (index == MD_SLICE6);
}
}
}
return (error);
}