/*
* 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 <assert.h>
#include <ctype.h>
#include <libdevinfo.h>
#include <mdiox.h>
#include <meta.h>
#include "meta_repartition.h"
#include "meta_set_prv.h"
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/lvm/md_mddb.h>
#include <sys/lvm/md_names.h>
#include <sys/lvm/md_crc.h>
#include <sys/lvm/md_convert.h>
typedef struct did_list {
void *rdid; /* real did if replicated set */
void *did; /* did stored in lb */
char *devname;
dev_t dev;
uint_t did_index;
char *minor_name;
char *driver_name;
int available;
struct did_list *next;
} did_list_t;
typedef struct replicated_disk {
void *old_devid;
void *new_devid;
struct replicated_disk *next;
} replicated_disk_t;
/*
* The current implementation limits the max device id length to 256 bytes.
* Should the max device id length be increased, this definition would have to
* be bumped up accordingly
*/
#define MAX_DEVID_LEN 256
/*
* We store a global list of all the replicated disks in the system. In
* order to prevent us from performing a linear search on this list, we
* store the disks in a two dimensional sparse array. The disks are bucketed
* based on the length of their device ids.
*/
static replicated_disk_t *replicated_disk_list[MAX_DEVID_LEN + 1] = {NULL};
/*
* The list of replicated disks is built just once and this flag is set
* once it's done
*/
int replicated_disk_list_built_pass1 = 0;
int replicated_disk_list_built_pass2 = 0;
int *replicated_disk_list_built;
static void free_did_list(did_list_t *did_listp);
/*
* Map logical blk to physical
*
* This is based on the routine of the same name in the md kernel module (see
* file md_mddb.c), with the following caveats:
*
* - The kernel routine works on in core master blocks, or mddb_mb_ic_t; this
* routine works instead on the mddb_mb_t read directly from the disk
*/
daddr_t
getphysblk(
mddb_block_t blk,
mddb_mb_t *mbp
)
{
/*
* Sanity check: is the block within range? If so, we then assume
* that the block range map in the master block is valid and
* consistent with the block count. Unfortunately, there is no
* reliable way to validate this assumption.
*/
if (blk >= mbp->mb_blkcnt || blk >= mbp->mb_blkmap.m_consecutive)
return ((daddr_t)-1);
return (mbp->mb_blkmap.m_firstblk + blk);
}
/*
* drive_append()
*
* Append to tail of linked list of md_im_drive_info_t.
*
* Will allocate space for new node and copy args into new space.
*
* Returns pointer to new node.
*/
static md_im_drive_info_t *
drive_append(
md_im_drive_info_t **midpp,
mddrivename_t *dnp,
did_list_t *nonrep_did_listp,
minor_t mnum,
md_timeval32_t timestamp,
md_im_replica_info_t *mirp
)
{
md_im_drive_info_t *midp;
int o_devid_sz;
int devid_sz;
for (; (*midpp != NULL); midpp = &((*midpp)->mid_next))
;
midp = *midpp = Zalloc(sizeof (md_im_drive_info_t));
midp->mid_dnp = dnp;
/*
* If rdid is not NULL then we know we are dealing with
* replicated diskset case. 'devid_sz' will always be the
* size of a valid devid which can be 'did' or 'rdid'
*/
if (nonrep_did_listp->rdid) {
devid_sz = devid_sizeof(nonrep_did_listp->rdid);
midp->mid_devid = (void *)Malloc(devid_sz);
(void) memcpy(midp->mid_devid, nonrep_did_listp->rdid,
devid_sz);
/*
* Also need to store the 'other' devid
*/
o_devid_sz = devid_sizeof((ddi_devid_t)(nonrep_did_listp->did));
midp->mid_o_devid = (void *)Malloc(o_devid_sz);
(void) memcpy(midp->mid_o_devid, nonrep_did_listp->did,
o_devid_sz);
midp->mid_o_devid_sz = o_devid_sz;
} else {
devid_sz = devid_sizeof(nonrep_did_listp->did);
midp->mid_devid = (void *)Malloc(devid_sz);
/*
* In the case of regular diskset, midp->mid_o_devid
* will be a NULL pointer
*/
(void) memcpy(midp->mid_devid, nonrep_did_listp->did, devid_sz);
}
midp->mid_devid_sz = devid_sz;
midp->mid_setcreatetimestamp = timestamp;
midp->mid_available = nonrep_did_listp->available;
if (nonrep_did_listp->minor_name) {
(void) strlcpy(midp->mid_minor_name,
nonrep_did_listp->minor_name, MDDB_MINOR_NAME_MAX);
}
midp->mid_mnum = mnum;
if (nonrep_did_listp->driver_name)
midp->mid_driver_name = Strdup(nonrep_did_listp->driver_name);
midp->mid_replicas = mirp;
if (nonrep_did_listp->devname)
midp->mid_devname = Strdup(nonrep_did_listp->devname);
return (midp);
}
/*
* drive_append_wrapper()
*
* Constant time append wrapper; the append function will always walk the list,
* this will take a tail argument and use the append function on just the tail
* node, doing the appropriate old-tail-next-pointer bookkeeping.
*/
static md_im_drive_info_t **
drive_append_wrapper(
md_im_drive_info_t **tailpp,
mddrivename_t *dnp,
did_list_t *nonrep_did_listp,
minor_t mnum,
md_timeval32_t timestamp,
md_im_replica_info_t *mirp
)
{
(void) drive_append(tailpp, dnp, nonrep_did_listp, mnum, timestamp,
mirp);
if ((*tailpp)->mid_next == NULL)
return (tailpp);
return (&((*tailpp)->mid_next));
}
/*
* replica_append()
*
* Append to tail of linked list of md_im_replica_info_t.
*
* Will allocate space for new node and copy args into new space.
*
* Returns pointer to new node.
*/
static md_im_replica_info_t *
replica_append(
md_im_replica_info_t **mirpp,
int flags,
daddr32_t offset,
daddr32_t length,
md_timeval32_t timestamp
)
{
md_im_replica_info_t *mirp;
for (; (*mirpp != NULL); mirpp = &((*mirpp)->mir_next))
;
mirp = *mirpp = Zalloc(sizeof (md_im_replica_info_t));
mirp->mir_flags = flags;
mirp->mir_offset = offset;
mirp->mir_length = length;
mirp->mir_timestamp = timestamp;
return (mirp);
}
/*
* replica_append_wrapper()
*
* Constant time append wrapper; the append function will always walk the list,
* this will take a tail argument and use the append function on just the tail
* node, doing the appropriate old-tail-next-pointer bookkeeping.
*/
static md_im_replica_info_t **
replica_append_wrapper(
md_im_replica_info_t **tailpp,
int flags,
daddr32_t offset,
daddr32_t length,
md_timeval32_t timestamp
)
{
(void) replica_append(tailpp, flags, offset, length, timestamp);
if ((*tailpp)->mir_next == NULL)
return (tailpp);
return (&(*tailpp)->mir_next);
}
/*
* map_replica_disk()
*
* Searches the device id list for a specific
* disk based on the locator block device id array index.
*
* Returns a pointer to the did_list node if a match was
* found or NULL otherwise.
*/
static did_list_t *
map_replica_disk(
did_list_t *did_listp,
int did_index
)
{
did_list_t *tailp = did_listp;
while (tailp != NULL) {
if (tailp->did_index == did_index)
return (tailp);
tailp = tailp->next;
}
/* not found, return failure */
return (NULL);
}
/*
* replicated_list_lookup()
*
* looks up a replicated disk entry in the global replicated disk list
* based upon the length of that disk's device id. returns the new device id
* for the disk.
* If you store the returned devid you must create a local copy.
*/
void *
replicated_list_lookup(
uint_t devid_len,
void *old_devid
)
{
replicated_disk_t *head = NULL;
assert(devid_len <= MAX_DEVID_LEN);
head = replicated_disk_list[devid_len];
if (head == NULL)
return (NULL);
do {
if (devid_compare((ddi_devid_t)old_devid,
(ddi_devid_t)head->old_devid) == 0)
return (head->new_devid);
head = head->next;
} while (head != NULL);
return (NULL);
}
/*
* replicated_list_insert()
*
* inserts a replicated disk entry into the global replicated disk list
*/
static void
replicated_list_insert(
size_t old_devid_len,
void *old_devid,
void *new_devid
)
{
replicated_disk_t *repl_disk, **first_entry;
void *repl_old_devid = NULL;
assert(old_devid_len <= MAX_DEVID_LEN);
repl_disk = Zalloc(sizeof (replicated_disk_t));
repl_old_devid = Zalloc(old_devid_len);
(void) memcpy(repl_old_devid, (void *)old_devid, old_devid_len);
repl_disk->old_devid = repl_old_devid;
repl_disk->new_devid = new_devid;
first_entry = &replicated_disk_list[old_devid_len];
if (*first_entry == NULL) {
*first_entry = repl_disk;
return;
}
repl_disk->next = *first_entry;
replicated_disk_list[old_devid_len] = repl_disk;
}
/*
* get_replica_disks()
*
* Will step through the locator records in the supplied locator block, and add
* each one with an active replica to a supplied list of md_im_drive_info_t, and
* add the appropriate replicas to the md_im_replica_info_t contained therein.
*/
static void
get_replica_disks(
md_im_set_desc_t *misp,
did_list_t *did_listp,
mddb_mb_t *mb,
mddb_lb_t *lbp,
md_error_t *ep
)
{
mddrivename_t *dnp;
int indx, on_list;
mdsetname_t *sp = metasetname(MD_LOCAL_NAME, ep);
int flags;
did_list_t *replica_disk;
daddr32_t offset;
daddr32_t length;
md_timeval32_t timestamp;
md_im_replica_info_t **mirpp = NULL;
md_im_drive_info_t **midpp = &misp->mis_drives;
md_im_drive_info_t *midp;
for (indx = 0; indx < lbp->lb_loccnt; indx++) {
on_list = 0;
if ((lbp->lb_locators[indx].l_flags == 0) ||
(lbp->lb_locators[indx].l_flags & MDDB_F_DELETED))
continue;
/*
* search the device id list for a
* specific ctds based on the locator
* block device id array index.
*/
replica_disk = map_replica_disk(did_listp, indx);
assert(replica_disk != NULL);
/*
* metadrivename() can fail for a slice name
* if there is not an existing mddrivename_t.
* So we use metadiskname() to strip the slice
* number.
*/
dnp = metadrivename(&sp, metadiskname(replica_disk->devname),
ep);
for (midp = misp->mis_drives; midp != NULL;
midp = midp->mid_next) {
if (dnp == midp->mid_dnp) {
/*
* You could get a dnp match, but if 1 disk
* is unavailable and the other isn't, they
* will have the same dnp due
* to the name being the same, but in fact
* are different disks.
*/
if (midp->mid_available ==
replica_disk->available) {
on_list = 1;
mirpp = &midp->mid_replicas;
break;
}
}
}
/*
* New on the list so add it
*/
if (!on_list) {
mddb_mb_t *mbp;
uint_t sliceno;
mdname_t *rsp;
int fd = -1;
mbp = Malloc(DEV_BSIZE);
/*
* If the disk isn't available, we don't
* want to try to read from it.
*/
if (replica_disk->available == MD_IM_DISK_AVAILABLE) {
/* determine the replica slice */
if (meta_replicaslice(dnp, &sliceno,
ep) != 0) {
Free(mbp);
continue;
}
/*
* if the replica slice size is zero,
* don't bother opening
*/
if (dnp->vtoc.parts[sliceno].size == 0) {
Free(mbp);
continue;
}
if ((rsp = metaslicename(dnp, sliceno,
ep)) == NULL) {
Free(mbp);
continue;
}
if ((fd = open(rsp->rname,
O_RDONLY| O_NDELAY)) < 0) {
Free(mbp);
continue;
}
/*
* a drive may not have a master block
*/
if (read_master_block(ep, fd, mbp,
DEV_BSIZE) <= 0) {
mdclrerror(ep);
Free(mbp);
(void) close(fd);
continue;
}
(void) close(fd);
}
midpp = drive_append_wrapper(midpp, dnp,
replica_disk,
meta_getminor(replica_disk->dev),
mbp->mb_setcreatetime, NULL);
mirpp = &((*midpp)->mid_replicas);
Free(mbp);
}
/*
* For either of these assertions to fail, it implies
* a NULL return from metadrivename() above. Since
* the args came from a presumed valid locator block,
* that's Bad.
*/
assert(midpp != NULL);
assert(mirpp != NULL);
/*
* Extract the parameters describing this replica.
*
* The magic "1" in the length calculation accounts
* for the length of the master block, in addition to
* the block count it describes. (The master block
* will always take up one block on the disk, and
* there will always only be one master block per
* replica, even though much of the code is structured
* to handle noncontiguous replicas.)
*/
flags = lbp->lb_locators[indx].l_flags;
offset = lbp->lb_locators[indx].l_blkno;
length = mb->mb_blkcnt + 1;
timestamp = mb->mb_setcreatetime;
mirpp = replica_append_wrapper(mirpp, flags,
offset, length, timestamp);
/*
* If we're here it means -
*
* we've added the disk to the list of
* disks.
*/
/*
* We need to bump up the number of active
* replica count for each such replica that is
* active so that it can be used later for replica
* quorum check.
*/
if (flags & MDDB_F_ACTIVE) {
misp->mis_active_replicas++;
}
}
}
/*
* append_pnm_rec()
*
* Append pnm_rec_t entry to list of physical devices in the diskset. Entry
* contains a mapping of n_key in NM namespace(or min_key in DID_NM namespace)
* to name of the physical device. This list will be used to ensure that the
* correct names of the physical devices are printed in the metastat output--the
* NM namespace might have stale information about where the physical devices
* were previously located when the diskset was last active.
*/
static void
append_pnm_rec(
pnm_rec_t **pnm,
mdkey_t min_key,
char *n_name
)
{
pnm_rec_t *tmp_pnm;
char *p;
int len;
if ((p = strrchr(n_name, '/')) != NULL)
p++;
/*
* Allocates pnm_rec_t record for the physical
* device.
*/
len = strlen(p) + 1; /* Length of name plus Null term */
tmp_pnm = Malloc(sizeof (pnm_rec_t) + len);
(void) strncpy(tmp_pnm->n_name, p, len);
tmp_pnm->n_key = min_key;
/*
* Adds new element to head of pnm_rec_t list.
*/
if (*pnm == NULL) {
tmp_pnm->next = NULL;
*pnm = tmp_pnm;
} else {
tmp_pnm->next = *pnm;
*pnm = tmp_pnm;
}
}
/*
* free_pnm_rec_list()
*
* Freeing all pnm_rec_t entries on the list of physical devices in the
* diskset.
*/
void
free_pnm_rec_list(pnm_rec_t **pnm)
{
pnm_rec_t *tmp_pnm, *rm_pnm;
for (tmp_pnm = *pnm; tmp_pnm != NULL; ) {
rm_pnm = tmp_pnm;
tmp_pnm = tmp_pnm->next;
Free(rm_pnm);
}
*pnm = NULL;
}
/*
* get_disks_from_didnamespace()
* This function was origionally called: get_nonreplica_disks()
*
* Extracts the disks without replicas from the locator name space and adds them
* to the supplied list of md_im_drive_info_t.
* If the print verbose option was given then this function will also
* correct the nm namespace so that the n_name is the right ctd name
*/
static void
get_disks_from_didnamespace(
md_im_set_desc_t *misp,
pnm_rec_t **pnm,
mddb_rb_t *nm,
mddb_rb_t *shrnm,
mddb_rb_t *did_nm,
mddb_rb_t *did_shrnm,
uint_t imp_flags,
int replicated,
md_error_t *ep
)
{
char *search_path = "/dev";
devid_nmlist_t *nmlist;
md_im_drive_info_t *midp, **midpp = &misp->mis_drives;
mddrivename_t *dnp;
mdsetname_t *sp = metasetname(MD_LOCAL_NAME, ep);
mddb_rb_t *rbp_did = did_nm;
mddb_rb_t *rbp_did_shr = did_shrnm;
mddb_rb_t *rbp_nm = nm;
mddb_rb_t *rbp_shr_nm = shrnm;
int on_list = 0;
struct devid_min_rec *did_rec;
struct devid_shr_rec *did_shr_rec;
struct nm_rec *namesp_rec;
struct nm_shr_rec *namesp_shr_rec;
struct did_shr_name *did;
struct did_min_name *min;
void *r_did; /* NULL if not a replicated diskset */
void *valid_did;
int avail = 0;
struct nm_name *nmp;
struct nm_shared_name *snmp;
mdkey_t drv_key, key, dev_key;
minor_t mnum = 0;
did_list_t *nonrep_did_listp;
size_t used_size, offset;
/*
* We got a pointer to an mddb record, which we expect to contain a
* name record; extract the pointer thereto.
*/
/* LINTED */
did_rec = (struct devid_min_rec *)((caddr_t)(&rbp_did->rb_data));
/* LINTED */
did_shr_rec = (struct devid_shr_rec *)
((caddr_t)(&rbp_did_shr->rb_data));
/* LINTED */
namesp_rec = (struct nm_rec *)((caddr_t)(&rbp_nm->rb_data));
/* LINTED */
namesp_shr_rec = (struct nm_shr_rec *)((caddr_t)(&rbp_shr_nm->rb_data));
/*
* Skip the nm_rec_hdr and iterate on the array of struct minor_name
* at the end of the devid_min_rec
*/
for (min = &did_rec->minor_name[0]; min->min_devid_key != 0;
/* LINTED */
min = (struct did_min_name *)((char *)min + DID_NAMSIZ(min))) {
on_list = 0;
r_did = NULL;
nonrep_did_listp = Zalloc(sizeof (struct did_list));
/*
* For a given DID_NM key, locate the corresponding device
* id from DID_NM_SHR
*/
for (did = &did_shr_rec->device_id[0]; did->did_key != 0;
/* LINTED */
did = (struct did_shr_name *)
((char *)did + DID_SHR_NAMSIZ(did))) {
/*
* We got a match, this is the device id we're
* looking for
*/
if (min->min_devid_key == did->did_key)
break;
}
if (did->did_key == 0) {
/* we didn't find a match */
assert(did->did_key != 0);
md_exit(NULL, 1);
}
/*
* If replicated diskset
*/
if (replicated) {
size_t new_devid_len, old_devid_len;
char *temp;
/*
* In this case, did->did_devid will
* be invalid so lookup the real one
*/
temp = replicated_list_lookup(did->did_size,
did->did_devid);
if (temp == NULL) {
/* we have a partial replicated set, fake it */
new_devid_len = did->did_size;
r_did = Zalloc(new_devid_len);
(void) memcpy(r_did, did->did_devid,
new_devid_len);
} else {
new_devid_len = devid_sizeof((ddi_devid_t)temp);
r_did = Zalloc(new_devid_len);
(void) memcpy(r_did, temp, new_devid_len);
}
valid_did = r_did;
nonrep_did_listp->rdid = Zalloc(new_devid_len);
(void) memcpy(nonrep_did_listp->rdid, r_did,
new_devid_len);
old_devid_len =
devid_sizeof((ddi_devid_t)did->did_devid);
nonrep_did_listp->did = Zalloc(old_devid_len);
(void) memcpy((void *)nonrep_did_listp->did,
(void *)did->did_devid, old_devid_len);
} else {
size_t new_devid_len;
valid_did = did->did_devid;
new_devid_len =
devid_sizeof((ddi_devid_t)did->did_devid);
nonrep_did_listp->did = Zalloc(new_devid_len);
(void) memcpy((void *)nonrep_did_listp->did,
(void *)did->did_devid, new_devid_len);
}
/*
* Get a ctds mapping for that device id.
* Since disk is being imported into this system,
* just use the first ctds in list.
*/
if (meta_deviceid_to_nmlist(search_path,
(ddi_devid_t)valid_did,
&min->min_name[0], &nmlist) == 0) {
/*
* We know the disk is available. Use the
* device information in nmlist.
*/
assert(nmlist[0].devname != NULL);
nonrep_did_listp->devname = Strdup(nmlist[0].devname);
nonrep_did_listp->available = MD_IM_DISK_AVAILABLE;
avail = 0;
mnum = meta_getminor(nmlist[0].dev);
devid_free_nmlist(nmlist);
} else {
/*
* The disk is not available. That means we need to
* use the (old) device information stored in the
* namespace.
*/
/* search in nm space for a match */
offset = sizeof (struct nm_rec) -
sizeof (struct nm_name);
used_size = namesp_rec->r_rec_hdr.r_used_size - offset;
for (nmp = &namesp_rec->r_name[0]; nmp->n_key != 0;
/* LINTED */
nmp = (struct nm_name *)((char *)nmp +
NAMSIZ(nmp))) {
if (nmp->n_key == min->min_key)
break;
used_size -= NAMSIZ(nmp);
if ((int)used_size <= 0) {
md_exit(NULL, 1);
}
}
if (nmp->n_key == 0) {
assert(nmp->n_key != 0);
md_exit(NULL, 1);
}
dev_key = nmp->n_dir_key;
snmp = &namesp_shr_rec->sr_name[0];
key = snmp->sn_key;
/*
* Use the namespace n_dir_key to look in the
* shared namespace. When we find the matching
* key, that is the devname and minor number we
* want.
*/
offset = sizeof (struct nm_shr_rec) -
sizeof (struct nm_shared_name);
used_size = namesp_shr_rec->sr_rec_hdr.r_used_size -
offset;
while (key != 0) {
if (dev_key == key) {
/*
* This complicated looking series
* of code creates a devname of the
* form <sn_name>/<n_name> which
* will look like /dev/dsk/c1t4d0s0.
*/
nonrep_did_listp->devname =
Zalloc(strlen(nmp->n_name) +
strlen(snmp->sn_name) + 2);
(void) strlcpy(
nonrep_did_listp->devname,
snmp->sn_name,
strlen(snmp->sn_name));
(void) strlcat(
nonrep_did_listp->devname, "/",
strlen(nmp->n_name) +
strlen(snmp->sn_name) + 2);
(void) strlcat(
nonrep_did_listp->devname,
nmp->n_name,
strlen(nmp->n_name) +
strlen(snmp->sn_name) + 2);
mnum = nmp->n_minor;
break;
}
/* LINTED */
snmp = (struct nm_shared_name *)((char *)snmp +
SHR_NAMSIZ(snmp));
key = snmp->sn_key;
used_size -= SHR_NAMSIZ(snmp);
if ((int)used_size <= 0) {
md_exit(NULL, 1);
}
}
if (key == 0) {
nonrep_did_listp->devname = NULL;
mnum = 0;
}
nonrep_did_listp->available = MD_IM_DISK_NOT_AVAILABLE;
nonrep_did_listp->minor_name = Strdup(min->min_name);
avail = 1;
drv_key = nmp->n_drv_key;
snmp = &namesp_shr_rec->sr_name[0];
key = snmp->sn_key;
/*
* Use the namespace n_drv_key to look in the
* shared namespace. When we find the matching
* key, that is the driver name for the disk.
*/
offset = sizeof (struct nm_shr_rec) -
sizeof (struct nm_shared_name);
used_size = namesp_shr_rec->sr_rec_hdr.r_used_size -
offset;
while (key != 0) {
if (drv_key == key) {
nonrep_did_listp->driver_name =
Strdup(snmp->sn_name);
break;
}
/* LINTED */
snmp = (struct nm_shared_name *)((char *)snmp +
SHR_NAMSIZ(snmp));
key = snmp->sn_key;
used_size -= SHR_NAMSIZ(snmp);
if ((int)used_size <= 0) {
md_exit(NULL, 1);
}
}
if (key == 0)
nonrep_did_listp->driver_name = NULL;
}
dnp = metadrivename(&sp,
metadiskname(nonrep_did_listp->devname), ep);
/*
* Add drive to pnm_rec_t list of physical devices for
* metastat output.
*/
if (imp_flags & META_IMP_VERBOSE) {
append_pnm_rec(pnm, min->min_key,
nonrep_did_listp->devname);
}
assert(dnp != NULL);
/* Is it already on the list? */
for (midp = misp->mis_drives; midp != NULL;
midp = midp->mid_next) {
if (midp->mid_dnp == dnp) {
if (midp->mid_available ==
nonrep_did_listp->available) {
on_list = 1;
break;
}
}
}
if (!on_list) {
mddb_mb_t *mbp;
uint_t sliceno;
mdname_t *rsp;
int fd = -1;
mbp = Malloc(DEV_BSIZE);
if (!avail) {
/* determine the replica slice */
if (meta_replicaslice(dnp, &sliceno,
ep) != 0) {
Free(mbp);
free_did_list(nonrep_did_listp);
continue;
}
/*
* if the replica slice size is zero,
* don't bother opening
*/
if (dnp->vtoc.parts[sliceno].size
== 0) {
Free(mbp);
free_did_list(nonrep_did_listp);
continue;
}
if ((rsp = metaslicename(dnp, sliceno,
ep)) == NULL) {
Free(mbp);
free_did_list(nonrep_did_listp);
continue;
}
if ((fd = open(rsp->rname,
O_RDONLY| O_NDELAY)) < 0) {
Free(mbp);
free_did_list(nonrep_did_listp);
continue;
}
/*
* a drive may not have a master block
*/
if (read_master_block(ep, fd, mbp,
DEV_BSIZE) <= 0) {
mdclrerror(ep);
Free(mbp);
free_did_list(nonrep_did_listp);
(void) close(fd);
continue;
}
(void) close(fd);
}
/*
* If it is replicated diskset,
* r_did will be non-NULL.
* Passing the devname as NULL because field
* is not currently used for a non-replica disk.
*/
midpp = drive_append_wrapper(midpp,
dnp, nonrep_did_listp,
mnum, mbp->mb_setcreatetime, NULL);
Free(mbp);
free_did_list(nonrep_did_listp);
}
free_did_list(nonrep_did_listp);
}
}
/*
* set_append()
*
* Append to tail of linked list of md_im_set_desc_t.
*
* Will allocate space for new node AND populate it by extracting disks with
* and without replicas from the locator blocks and locator namespace.
*
* Returns pointer to new node.
*/
static md_im_set_desc_t *
set_append(
md_im_set_desc_t **mispp,
did_list_t *did_listp,
mddb_mb_t *mb,
mddb_lb_t *lbp,
mddb_rb_t *nm,
mddb_rb_t *shrnm,
pnm_rec_t **pnm,
mddb_rb_t *did_nm,
mddb_rb_t *did_shrnm,
uint_t imp_flags,
md_error_t *ep
)
{
md_im_set_desc_t *misp;
set_t setno = mb->mb_setno;
int partial = imp_flags & MD_IM_PARTIAL_DISKSET;
int replicated = imp_flags & MD_IM_SET_REPLICATED;
/* run to end of list */
for (; (*mispp != NULL); mispp = &((*mispp)->mis_next))
;
/* allocate new list element */
misp = *mispp = Zalloc(sizeof (md_im_set_desc_t));
if (replicated)
misp->mis_flags = MD_IM_SET_REPLICATED;
misp->mis_oldsetno = setno;
misp->mis_partial = partial;
/* Get the disks with and without replicas */
get_replica_disks(misp, did_listp, mb, lbp, ep);
if (nm != NULL && did_nm != NULL && did_shrnm != NULL) {
get_disks_from_didnamespace(misp, pnm, nm, shrnm, did_nm,
did_shrnm, imp_flags, replicated, ep);
}
/*
* An error in this struct could come from either of
* the above routines;
* in both cases, we want to pass it back on up.
*/
return (misp);
}
/*
* add_disk_names()
*
* Iterator to walk the minor node tree of the device snapshot, adding only the
* first non-block instance of each non-cdrom minor node to a list of disks.
*/
static int
add_disk_names(di_node_t node, di_minor_t minor, void *args)
{
char *search_path = "/dev";
ddi_devid_t devid = di_devid(node);
devid_nmlist_t *nm;
char *min = di_minor_name(minor);
md_im_names_t *cnames = (md_im_names_t *)args;
static di_node_t save_node = NULL;
/*
* skip CD devices
* If a device does not have a device id, we can't
* do anything with it so just exclude it from our
* list.
*
* This would also encompass CD devices and floppy
* devices that don't have a device id.
*/
if (devid == NULL) {
return (DI_WALK_CONTINUE);
}
/* char disk devices (as opposed to block) */
if (di_minor_spectype(minor) == S_IFCHR) {
/* only first occurrence (slice 0) of each instance */
if (save_node == NULL || node != save_node) {
save_node = node;
if (meta_deviceid_to_nmlist(search_path, devid,
min, &nm) == 0) {
int index = cnames->min_count++;
assert(nm->devname != NULL);
cnames->min_names =
Realloc(cnames->min_names,
cnames->min_count *
sizeof (char *));
assert(cnames->min_names != NULL);
cnames->min_names[index] =
metadiskname(nm->devname);
devid_free_nmlist(nm);
}
}
}
return (DI_WALK_CONTINUE);
}
/*
* meta_list_disks()
*
* Snapshots the device tree and extracts disk devices from the snapshot.
*/
int
meta_list_disks(md_error_t *ep, md_im_names_t *cnames)
{
di_node_t root_node;
assert(cnames != NULL);
cnames->min_count = 0;
cnames->min_names = NULL;
if ((root_node = di_init("/", DINFOCPYALL|DINFOFORCE))
== DI_NODE_NIL) {
return (mdsyserror(ep, errno, NULL));
}
(void) di_walk_minor(root_node, DDI_NT_BLOCK, 0, cnames,
add_disk_names);
di_fini(root_node);
return (0);
}
/*
* meta_imp_drvused
*
* Checks if given drive is mounted, swapped, part of disk configuration
* or in use by SVM. ep also has error code set up if drive is in use.
*
* Returns 1 if drive is in use.
* Returns 0 if drive is not in use.
*/
int
meta_imp_drvused(
mdsetname_t *sp,
mddrivename_t *dnp,
md_error_t *ep
)
{
md_error_t status = mdnullerror;
md_error_t *db_ep = &status;
/*
* We pass in db_ep to meta_setup_db_locations
* and never ever use the error contained therein
* because all we're interested in is a check to
* see whether any local metadbs are present.
*/
if ((meta_check_drivemounted(sp, dnp, ep) != 0) ||
(meta_check_driveswapped(sp, dnp, ep) != 0) ||
(((meta_setup_db_locations(db_ep) == 0) &&
((meta_check_drive_inuse(sp, dnp, 1, ep) != 0) ||
(meta_check_driveinset(sp, dnp, ep) != 0))))) {
return (1);
} else {
return (0);
}
}
/*
* meta_prune_cnames()
*
* Removes in-use disks from the list prior to further processing.
*
* Return value depends on err_on_prune flag: if set, and one or more disks
* are pruned, the return list will be the pruned disks. If not set, or if no
* disks are pruned, the return list will be the unpruned disks.
*/
mddrivenamelist_t *
meta_prune_cnames(
md_error_t *ep,
md_im_names_t *cnames,
int err_on_prune
)
{
int d;
int fcount = 0;
mddrivenamelist_t *dnlp = NULL;
mddrivenamelist_t **dnlpp = &dnlp;
mddrivenamelist_t *fdnlp = NULL;
mddrivenamelist_t **fdnlpp = &fdnlp;
mdsetname_t *sp = metasetname(MD_LOCAL_NAME, ep);
for (d = 0; d < cnames->min_count; ++d) {
mddrivename_t *dnp;
dnp = metadrivename(&sp, cnames->min_names[d], ep);
if (dnp == NULL) {
/*
* Assuming we're interested in knowing about
* whatever error occurred, but not in stopping.
*/
mde_perror(ep, cnames->min_names[d]);
mdclrerror(ep);
continue;
}
/*
* Check if the drive is inuse.
*/
if (meta_imp_drvused(sp, dnp, ep)) {
fdnlpp = meta_drivenamelist_append_wrapper(fdnlpp, dnp);
fcount++;
mdclrerror(ep);
} else {
dnlpp = meta_drivenamelist_append_wrapper(dnlpp, dnp);
}
}
if (fcount) {
if (err_on_prune) {
(void) mddserror(ep, MDE_DS_DRIVEINUSE, 0,
NULL, fdnlp->drivenamep->cname, NULL);
metafreedrivenamelist(dnlp);
return (fdnlp);
}
metafreedrivenamelist(fdnlp);
}
return (dnlp);
}
/*
* read_master_block()
*
* Returns:
* < 0 for failure
* 0 for no valid master block
* 1 for valid master block
*
* The supplied buffer will be filled in for EITHER 0 or 1.
*/
int
read_master_block(
md_error_t *ep,
int fd,
void *bp,
int bsize
)
{
mddb_mb_t *mbp = bp;
int rval = 1;
assert(bp != NULL);
if (lseek(fd, (off_t)dbtob(16), SEEK_SET) < 0)
return (mdsyserror(ep, errno, NULL));
if (read(fd, bp, bsize) != bsize)
return (mdsyserror(ep, errno, NULL));
/*
* The master block magic number can either be MDDB_MAGIC_MB in
* the case of a real master block, or, it can be MDDB_MAGIC_DU
* in the case of a dummy master block
*/
if ((mbp->mb_magic != MDDB_MAGIC_MB) &&
(mbp->mb_magic != MDDB_MAGIC_DU)) {
rval = 0;
(void) mdmddberror(ep, MDE_DB_MASTER, 0, 0, 0, NULL);
}
if (mbp->mb_revision != MDDB_REV_MB) {
rval = 0;
}
return (rval);
}
/*
* read_locator_block()
*
* Returns:
* < 0 for failure
* 0 for no valid locator block
* 1 for valid locator block
*/
int
read_locator_block(
md_error_t *ep,
int fd,
mddb_mb_t *mbp,
void *bp,
int bsize
)
{
mddb_lb_t *lbp = bp;
assert(bp != NULL);
if (lseek(fd, (off_t)dbtob(mbp->mb_blkmap.m_firstblk), SEEK_SET) < 0)
return (mdsyserror(ep, errno, NULL));
if (read(fd, bp, bsize) != bsize)
return (mdsyserror(ep, errno, NULL));
return ((lbp->lb_magic == MDDB_MAGIC_LB) ? 1 : 0);
}
int
phys_read(
md_error_t *ep,
int fd,
mddb_mb_t *mbp,
daddr_t blk,
void *bp,
int bcount
)
{
daddr_t pblk;
if ((pblk = getphysblk(blk, mbp)) < 0)
return (mdmddberror(ep, MDE_DB_BLKRANGE, NODEV32,
MD_LOCAL_SET, blk, NULL));
if (lseek(fd, (off_t)dbtob(pblk), SEEK_SET) < 0)
return (mdsyserror(ep, errno, NULL));
if (read(fd, bp, bcount) != bcount)
return (mdsyserror(ep, errno, NULL));
return (bcount);
}
/*
* read_locator_block_did()
*
* Returns:
* < 0 for failure
* 0 for no valid locator name struct
* 1 for valid locator name struct
*/
int
read_locator_block_did(
md_error_t *ep,
int fd,
mddb_mb_t *mbp,
mddb_lb_t *lbp,
void *bp,
int bsize
)
{
int lb_didfirstblk = lbp->lb_didfirstblk;
mddb_did_blk_t *lbdidp = bp;
int rval;
assert(bp != NULL);
if ((rval = phys_read(ep, fd, mbp, lb_didfirstblk, bp, bsize)) < 0)
return (rval);
return ((lbdidp->blk_magic == MDDB_MAGIC_DI) ? 1 : 0);
}
/*
* read_locator_names()
*
* Returns:
* < 0 for failure
* 0 for no valid locator name struct
* 1 for valid locator name struct
*/
int
read_locator_names(
md_error_t *ep,
int fd,
mddb_mb_t *mbp,
mddb_lb_t *lbp,
void *bp,
int bsize
)
{
int lnfirstblk = lbp->lb_lnfirstblk;
mddb_ln_t *lnp = bp;
int rval;
assert(bp != NULL);
if ((rval = phys_read(ep, fd, mbp, lnfirstblk, bp, bsize)) < 0)
return (rval);
return ((lnp->ln_magic == MDDB_MAGIC_LN) ? 1 : 0);
}
int
read_database_block(
md_error_t *ep,
int fd,
mddb_mb_t *mbp,
int dbblk,
void *bp,
int bsize
)
{
mddb_db_t *dbp = bp;
int rval;
assert(bp != NULL);
if ((rval = phys_read(ep, fd, mbp, dbblk, bp, bsize)) < 0)
return (rval);
return ((dbp->db_magic == MDDB_MAGIC_DB) ? 1 : 0);
}
int
read_loc_didblks(
md_error_t *ep,
int fd,
mddb_mb_t *mbp,
int didblk,
void *bp,
int bsize
)
{
mddb_did_blk_t *didbp = bp;
int rval;
assert(bp != NULL);
if ((rval = phys_read(ep, fd, mbp, didblk, bp, bsize)) < 0)
return (rval);
return ((didbp->blk_magic == MDDB_MAGIC_DI) ? 1 : 0);
}
int
read_loc_didinfo(
md_error_t *ep,
int fd,
mddb_mb_t *mbp,
int infoblk,
void *bp,
int bsize
)
{
int rval = 1;
mddb_did_info_t *infop = bp;
assert(bp != NULL);
if ((rval = phys_read(ep, fd, mbp, infoblk, bp, bsize)) < 0)
return (rval);
return ((infop->info_flags & MDDB_DID_EXISTS) ? 1 : 0);
}
/*
* meta_nm_rec()
*
* Return the DE corresponding to the requested namespace record type.
* Modifies dbp to have a firstentry if one isn't there.
*/
static mddb_de_t *
meta_nm_rec(mddb_db_t *dbp, mddb_type_t rectype)
{
mddb_de_t *dep;
int desize;
if (dbp->db_firstentry != NULL) {
/* LINTED */
dep = (mddb_de_t *)((caddr_t)(&dbp->db_firstentry)
+ sizeof (dbp->db_firstentry));
dbp->db_firstentry = dep;
while (dep && dep->de_next) {
desize = sizeof (*dep) - sizeof (dep->de_blks) +
sizeof (daddr_t) * dep->de_blkcount;
/* LINTED */
dep->de_next = (mddb_de_t *)
((caddr_t)dep + desize);
dep = dep->de_next;
}
}
for (dep = dbp->db_firstentry; dep != NULL; dep = dep->de_next) {
if (dep->de_type1 == rectype)
break;
}
return (dep);
}
/*
* read_nm_rec()
*
* Reads the NM, NM_DID or NM_DID_SHR record in the mddb and stores the
* configuration data in the buffer 'nm'
*
* Returns:
* < 0 for failure
* 0 for no valid NM/DID_NM/DID_NM_SHR record
* 1 for valid NM/DID_NM/DID_NM_SHR record
*
*/
static int
read_nm_rec(
md_error_t *ep,
int fd,
mddb_mb_t *mbp,
mddb_lb_t *lbp,
char **nm,
mddb_type_t rectype,
char *diskname
)
{
int cnt, dbblk, rval = 0;
char db[DEV_BSIZE];
mddb_de_t *dep;
/*LINTED*/
mddb_db_t *dbp = (mddb_db_t *)&db;
char *tmpnm = NULL;
daddr_t pblk;
for (dbblk = lbp->lb_dbfirstblk;
dbblk != 0;
dbblk = dbp->db_nextblk) {
if ((rval = read_database_block(ep, fd, mbp, dbblk, dbp,
sizeof (db))) <= 0)
return (rval);
/*
* Locate NM/DID_NM/DID_NM_SHR record. Normally there is
* only one record per mddb. There is a rare case when we
* can't expand the record. If this is the case then we
* will have multiple NM/DID_NM/DID_NM_SHR records linked
* with r_next_recid.
*
* For now assume the normal case and handle the extended
* namespace in Phase 2.
*/
if ((dep = meta_nm_rec(dbp, rectype)) != NULL)
break;
}
/* If meta_nm_rec() never succeeded, bail out */
if (dep == NULL)
return (0);
/* Read in the appropriate record and return configurations */
tmpnm = (char *)Zalloc(dbtob(dep->de_blkcount));
*nm = tmpnm;
for (cnt = 0; cnt < dep->de_blkcount; cnt++) {
if ((pblk = getphysblk(dep->de_blks[cnt], mbp)) < 0) {
rval = mdmddberror(ep, MDE_DB_BLKRANGE,
NODEV32, MD_LOCAL_SET,
dep->de_blks[cnt], diskname);
return (rval);
}
if (lseek(fd, (off_t)dbtob(pblk), SEEK_SET) < 0) {
rval = mdsyserror(ep, errno, diskname);
return (rval);
}
if (read(fd, tmpnm, DEV_BSIZE) != DEV_BSIZE) {
rval = mdsyserror(ep, errno, diskname);
return (rval);
}
tmpnm += DEV_BSIZE;
}
return (1);
}
/*
* is_replicated
*
* Determines whether a disk has been replicated or not. It checks to see
* if the device id stored in the master block is the same as the device id
* registered for that disk on the current system. If the two device ids are
* different, then we know that the disk has been replicated.
*
* If need_devid is set and the disk is replicated, fill in the new_devid.
* Also, if need_devid is set, this routine allocates memory for the device
* ids; the caller of this routine is responsible for free'ing up the memory.
*
* Returns:
* MD_IM_SET_REPLICATED if it's a replicated disk
* 0 if it's not a replicated disk
*/
static int
is_replicated(
int fd,
mddb_mb_t *mbp,
int need_devid,
void **new_devid
)
{
ddi_devid_t current_devid;
int retval = 0;
size_t new_devid_len;
if (mbp->mb_devid_magic != MDDB_MAGIC_DE)
return (retval);
if (devid_get(fd, &current_devid) != 0)
return (retval);
if (devid_compare((ddi_devid_t)mbp->mb_devid, current_devid) != 0)
retval = MD_IM_SET_REPLICATED;
if (retval && need_devid) {
new_devid_len = devid_sizeof(current_devid);
*new_devid = Zalloc(new_devid_len);
(void) memcpy(*new_devid, (void *)current_devid, new_devid_len);
}
devid_free(current_devid);
return (retval);
}
/*
* free_replicated_disks_list()
*
* this frees up all the memory allocated by build_replicated_disks_list
*/
static void
free_replicated_disks_list()
{
replicated_disk_t **repl_disk, *temp;
int index;
for (index = 0; index <= MAX_DEVID_LEN; index++) {
repl_disk = &replicated_disk_list[index];
while (*repl_disk != NULL) {
temp = *repl_disk;
*repl_disk = (*repl_disk)->next;
Free(temp->old_devid);
Free(temp->new_devid);
Free(temp);
}
}
}
/*
* build_replicated_disks_list()
*
* Builds a list of disks that have been replicated using either a
* remote replication or a point-in-time replication software. The
* list is stored as a two dimensional sparse array.
*
* Returns
* 1 on success
* 0 on failure
*/
int
build_replicated_disks_list(
md_error_t *ep,
mddrivenamelist_t *dnlp
)
{
uint_t sliceno;
int fd = -1;
mddrivenamelist_t *dp;
mdname_t *rsp;
mddb_mb_t *mbp;
mbp = Malloc(DEV_BSIZE);
for (dp = dnlp; dp != NULL; dp = dp->next) {
mddrivename_t *dnp;
void *new_devid;
dnp = dp->drivenamep;
/* determine the replica slice */
if (meta_replicaslice(dnp, &sliceno, ep) != 0)
continue;
/*
* if the replica slice size is zero, don't bother opening
*/
if (dnp->vtoc.parts[sliceno].size == 0)
continue;
if ((rsp = metaslicename(dnp, sliceno, ep)) == NULL)
continue;
if ((fd = open(rsp->rname, O_RDONLY| O_NDELAY)) < 0)
return (mdsyserror(ep, errno, rsp->rname));
/* a drive may not have a master block so we just continue */
if (read_master_block(ep, fd, mbp, DEV_BSIZE) <= 0) {
(void) close(fd);
mdclrerror(ep);
continue;
}
if (is_replicated(fd, mbp, 1, &new_devid)) {
replicated_list_insert(mbp->mb_devid_len,
mbp->mb_devid, new_devid);
}
(void) close(fd);
}
*replicated_disk_list_built = 1;
Free(mbp);
return (1);
}
/*
* free_did_list()
*
* Frees the did_list allocated as part of build_did_list
*/
static void
free_did_list(
did_list_t *did_listp
)
{
did_list_t *temp, *head;
head = did_listp;
while (head != NULL) {
temp = head;
head = head->next;
if (temp->rdid)
Free(temp->rdid);
if (temp->did)
Free(temp->did);
if (temp->devname)
Free(temp->devname);
if (temp->minor_name)
Free(temp->minor_name);
if (temp->driver_name)
Free(temp->driver_name);
Free(temp);
}
}
/*
* meta_free_im_replica_info
*
* Frees the md_im_replica_info list
*/
static void
meta_free_im_replica_info(
md_im_replica_info_t *mirp
)
{
md_im_replica_info_t *r, *temp;
r = mirp;
while (r != NULL) {
temp = r;
r = r->mir_next;
Free(temp);
}
}
/*
* meta_free_im_drive_info
*
* Frees the md_im_drive_info list
*/
static void
meta_free_im_drive_info(
md_im_drive_info_t *midp
)
{
md_im_drive_info_t *d, *temp;
d = midp;
while (d != NULL) {
temp = d;
d = d->mid_next;
if (temp->mid_available & MD_IM_DISK_NOT_AVAILABLE)
/*
* dnp is not on the drivenamelist and is a temp
* dnp for metaimport if the disk is unavailable.
* We need to specifically free it because of this.
* If the disk is available, standard drivelist freeing
* will kick in so we don't need to do it.
*/
metafreedrivename(temp->mid_dnp);
if (temp->mid_devid)
Free(temp->mid_devid);
if (temp->mid_o_devid)
Free(temp->mid_o_devid);
if (temp->mid_driver_name)
Free(temp->mid_driver_name);
if (temp->mid_devname)
Free(temp->mid_devname);
if (temp->mid_replicas) {
meta_free_im_replica_info(temp->mid_replicas);
temp->mid_replicas = NULL;
}
if (temp->overlap) {
meta_free_im_drive_info(temp->overlap);
temp->overlap = NULL;
}
Free(temp);
}
}
/*
* meta_free_im_set_desc
*
* Frees the md_im_set_desc_t list
*/
void
meta_free_im_set_desc(
md_im_set_desc_t *misp
)
{
md_im_set_desc_t *s, *temp;
s = misp;
while (s != NULL) {
temp = s;
s = s->mis_next;
if (temp->mis_drives) {
meta_free_im_drive_info(temp->mis_drives);
temp->mis_drives = NULL;
}
Free(temp);
}
}
/*
* build_did_list()
*
* Build a list of device ids corresponding to disks in the locator block.
* Memory is allocated here for the nodes in the did_list. The callers of
* this routine must also call free_did_list to free up the memory after
* they're done.
*
* Returns:
* < 0 for failure
* 0 for no valid locator block device id array
* 1 for valid locator block device id array
* ENOTSUP partial diskset, not all disks in a diskset on the
* system where import is being executed
*/
static int
build_did_list(
md_error_t *ep,
int fd,
mddb_mb_t *mb,
mddb_lb_t *lbp,
mddb_did_blk_t *lbdidp,
mddb_ln_t *lnp,
did_list_t **did_listp,
int replicated
)
{
char *search_path = "/dev";
char *minor_name;
int rval, cnt;
devid_nmlist_t *nm;
uint_t did_info_length = 0;
uint_t did_info_firstblk = 0;
did_list_t *new, *head = NULL;
char *bp = NULL, *temp;
mddb_did_info_t *did_info = NULL;
void *did = NULL;
size_t new_devid_len;
int partial = 0;
int partial_replicated = 0;
for (cnt = 0; cnt < MDDB_NLB; cnt++) {
partial_replicated = 0;
did_info = &lbdidp->blk_info[cnt];
if (!(did_info->info_flags & MDDB_DID_EXISTS))
continue;
new = Zalloc(sizeof (did_list_t));
new->did = Zalloc(did_info->info_length);
/*
* If we can re-use the buffer that has already been
* read in then just use it. Otherwise free
* the previous one and alloc a new one
*/
if (did_info->info_firstblk != did_info_firstblk) {
did_info_length = dbtob(did_info->info_blkcnt);
did_info_firstblk = did_info->info_firstblk;
if (bp)
Free(bp);
bp = temp = Zalloc(did_info_length);
if ((rval = phys_read(ep, fd, mb, did_info_firstblk,
(void *)bp, did_info_length)) < 0)
return (rval);
} else {
temp = bp;
}
temp += did_info->info_offset;
(void) memcpy(new->did, temp, did_info->info_length);
new->did_index = cnt;
minor_name = did_info->info_minor_name;
/*
* If we are not able to find the ctd mapping corresponding
* to a given device id, it probably means the device id in
* question is not registered with the system.
*
* Highly likely that the only time this happens, we've hit
* a case where not all the disks that are a part of the
* diskset were moved before importing the diskset.
*
* If set is a replicated diskset, then the device id we get
* from 'lb' will be the 'other' did and we need to lookup
* the real one before we call this routine.
*/
if (replicated) {
temp = replicated_list_lookup(did_info->info_length,
new->did);
if (temp == NULL) {
/* we have a partial replicated set, fake it */
new_devid_len = devid_sizeof((ddi_devid_t)new->did);
new->rdid = Zalloc(new_devid_len);
(void) memcpy(new->rdid, new->did, new_devid_len);
did = new->rdid;
partial_replicated = 1;
} else {
new_devid_len = devid_sizeof((ddi_devid_t)temp);
new->rdid = Zalloc(new_devid_len);
(void) memcpy(new->rdid, temp, new_devid_len);
did = new->rdid;
}
} else {
did = new->did;
}
if (devid_valid((ddi_devid_t)(did)) == 0) {
return (-1);
}
if (partial_replicated || meta_deviceid_to_nmlist(search_path,
(ddi_devid_t)did, minor_name, &nm) != 0) {
int len = 0;
/*
* Partial diskset case. We'll need to get the
* device information from the metadb instead
* of the output (nm) of meta_deviceid_to_nmlist.
*/
len = strlen(lnp->ln_prefixes[0].pre_data) +
strlen(lnp->ln_suffixes[0][cnt].suf_data) + 2;
new->devname = Zalloc(len);
(void) strlcpy(new->devname,
lnp->ln_prefixes[0].pre_data,
strlen(lnp->ln_prefixes[0].pre_data) + 1);
(void) strlcat(new->devname, "/", len);
(void) strlcat(new->devname,
lnp->ln_suffixes[0][cnt].suf_data, len);
new->minor_name = Strdup(minor_name);
new->next = head;
new->available = MD_IM_DISK_NOT_AVAILABLE;
new->driver_name = Strdup(lbp->lb_drvnm[0].dn_data);
new->dev = lbp->lb_locators[cnt].l_dev;
head = new;
partial = ENOTSUP;
continue;
}
/*
* Disk is there. Grab device information from nm structure.
*/
assert(nm->devname != NULL);
new->devname = Strdup(nm->devname);
new->dev = nm->dev;
new->minor_name = Strdup(minor_name);
new->available = MD_IM_DISK_AVAILABLE;
devid_free_nmlist(nm);
new->next = head;
head = new;
}
/* Free the last bp */
if (bp)
Free(bp);
*did_listp = head;
if (partial)
return (partial);
return (1);
}
/*
* check_nm_disks
* Checks the disks listed in the shared did namespace to see if they
* are accessable on the system. If not, return ENOTSUP error to
* indicate we have a partial diskset.
* Returns:
* < 0 for failure
* 0 success
* ENOTSUP partial diskset, not all disks in a diskset on the
* system where import is being executed
*/
static int
check_nm_disks(
struct devid_min_rec *did_nmp,
struct devid_shr_rec *did_shrnmp
)
{
char *search_path = "/dev";
char *minor_name = NULL;
uint_t used_size, min_used_size;
ddi_devid_t did;
devid_nmlist_t *nm;
void *did_min_namep;
void *did_shr_namep;
size_t did_nsize, did_shr_nsize;
used_size = did_shrnmp->did_rec_hdr.r_used_size -
sizeof (struct nm_rec_hdr);
min_used_size = did_nmp->min_rec_hdr.r_used_size -
sizeof (struct nm_rec_hdr);
did_shr_namep = (void *)(&did_shrnmp->device_id[0]);
while (used_size > (int)sizeof (struct did_shr_name)) {
did_min_namep = (void *)(&did_nmp->minor_name[0]);
/* grab device id and minor name from the shared spaces */
did = (ddi_devid_t)(((struct did_shr_name *)
did_shr_namep)->did_devid);
if (devid_valid(did) == 0) {
return (-1);
}
/*
* We need to check that the DID_NM and DID_SHR_NM are in
* sync. It is possible that we took a panic between writing
* the two areas to disk. This would be cleaned up on the
* next snarf but we don't know for sure that snarf has even
* happened since we're reading from disk.
*/
while (((struct did_shr_name *)did_shr_namep)->did_key !=
((struct did_min_name *)did_min_namep)->min_devid_key) {
did_nsize = DID_NAMSIZ((struct did_min_name *)
did_min_namep);
did_min_namep = ((void *)((char *)did_min_namep +
did_nsize));
min_used_size -= did_nsize;
if (min_used_size < (int)sizeof (struct did_min_name))
continue;
}
minor_name = ((struct did_min_name *)did_min_namep)->min_name;
/*
* Try to find disk in the system. If we can't find the
* disk, we have a partial diskset.
*/
if ((meta_deviceid_to_nmlist(search_path,
did, minor_name, &nm)) != 0) {
/* Partial diskset detected */
return (ENOTSUP);
}
devid_free_nmlist(nm);
used_size -= DID_SHR_NAMSIZ((struct did_shr_name *)
did_shr_namep);
/* increment to next item in the shared spaces */
did_shr_nsize = DID_SHR_NAMSIZ((struct did_shr_name *)
did_shr_namep);
did_shr_namep = ((void *)((char *)did_shr_namep +
did_shr_nsize));
}
return (0);
}
/*
* report_metadb_info()
*
* Generates metadb output for the diskset.
*
*/
static void
report_metadb_info(
md_im_set_desc_t *misp,
char *indent
)
{
md_im_drive_info_t *d;
md_im_replica_info_t *r;
char *unk_str = "";
int i;
(void) printf("%s\t%5.5s\t\t%9.9s\t%11.11s\n", indent, gettext("flags"),
gettext("first blk"), gettext("block count"));
unk_str = gettext("unknown");
/*
* Looping through all drives in the diskset to print
* out information about the drive and if the verbose
* option is set print out replica data.
*/
for (d = misp->mis_drives; d != NULL; d = d->mid_next) {
if (d->mid_replicas != NULL) {
for (r = d->mid_replicas; r != NULL;
r = r->mir_next) {
(void) printf("%s", indent);
for (i = 0; i < MDDB_FLAGS_LEN; i++) {
if (r->mir_flags & (1 << i)) {
(void) putchar(
MDDB_FLAGS_STRING[i]);
} else {
(void) putchar(' ');
}
}
if ((r->mir_offset == -1) && (r->mir_length
== -1)) {
(void) printf("%7.7s\t\t%7.7s\t",
unk_str, unk_str);
} else if (r->mir_length == -1) {
(void) printf("%i\t\t%7.7s\t",
r->mir_offset, unk_str);
} else {
(void) printf("%i\t\t%i\t",
r->mir_offset, r->mir_length);
}
(void) printf("\t%s\n",
d->mid_devname);
}
}
}
(void) printf("\n");
}
/*
* meta_replica_quorum will determine if the disks in the set to be
* imported have enough valid replicas to have quorum.
*
* RETURN:
* -1 Set doesn't have quorum
* 0 Set does have quorum
*/
int
meta_replica_quorum(
md_im_set_desc_t *misp
)
{
md_im_drive_info_t *midp;
md_im_replica_info_t *midr;
int replica_count = 0;
for (midp = misp->mis_drives; midp != NULL;
midp = midp->mid_next) {
if (midp->mid_available == MD_IM_DISK_NOT_AVAILABLE)
continue;
/*
* The drive is okay. Now count its replicas
*/
for (midr = midp->mid_replicas; midr != NULL;
midr = midr->mir_next) {
replica_count++;
}
}
if (misp->mis_active_replicas & 1) {
/* odd number of replicas */
if (replica_count < (misp->mis_active_replicas + 1)/2)
return (-1);
} else {
/* even number of replicas */
if (replica_count <= ((misp->mis_active_replicas + 1)/2))
return (-1);
}
return (0);
}
/*
* Choose the best drive to use for the metaimport command.
*/
md_im_drive_info_t *
pick_good_disk(md_im_set_desc_t *misp)
{
md_timeval32_t *setcrtime; /* set creation time */
md_im_drive_info_t *good_disk = NULL;
md_im_drive_info_t *midp = NULL;
md_im_replica_info_t *mirp;
setcrtime = &(misp->mis_drives->mid_replicas->mir_timestamp);
for (midp = misp->mis_drives; (midp != NULL) && (good_disk == NULL);
midp = midp->mid_next) {
/* drive must be available */
if (midp->mid_available == MD_IM_DISK_NOT_AVAILABLE) {
continue;
}
for (mirp = midp->mid_replicas; mirp != NULL;
mirp = mirp->mir_next) {
/* replica must be active to be a good one */
if (mirp->mir_flags & MDDB_F_ACTIVE) {
if ((setcrtime->tv_sec ==
midp-> mid_setcreatetimestamp.tv_sec) &&
(setcrtime->tv_usec ==
midp->mid_setcreatetimestamp.tv_usec)) {
good_disk = midp;
break;
}
}
}
}
return (good_disk);
}
/*
* report_set_info()
*
* Returns:
* < 0 for failure
* 0 for success
*
*/
static int
report_set_info(
md_im_set_desc_t *misp,
mddb_mb_t *mb,
mddb_lb_t *lbp,
mddb_rb_t *nm,
pnm_rec_t **pnm,
mdname_t *rsp,
int fd,
uint_t imp_flags,
int set_count,
int overlap,
md_im_drive_info_t *overlap_disks,
md_error_t *ep
)
{
int rval = 0;
md_im_drive_info_t *d;
md_im_drive_info_t *good_disk = NULL;
int i;
int in = META_INDENT;
char indent[MAXPATHLEN];
md_timeval32_t lastaccess; /* stores last modified timestamp */
int has_overlap = 0;
int no_quorum = 0;
int partial = 0;
/* Calculates the correct indentation. */
indent[0] = 0;
for (i = 0; i < in; i++)
(void) strlcat(indent, " ", sizeof (indent));
/*
* This will print before the information for the first diskset
* if the verbose option was set.
*/
if (set_count == 1) {
if (imp_flags & META_IMP_REPORT) {
(void) printf("\n%s:\n\n",
gettext("Disksets eligible for import"));
}
}
partial = misp->mis_partial;
good_disk = pick_good_disk(misp);
if (good_disk == NULL) {
return (rval);
}
/*
* Make the distinction between a regular diskset and
* a replicated diskset. Also make the distinction
* between a partial vs. full diskset.
*/
if (partial == MD_IM_PARTIAL_DISKSET) {
if (misp->mis_flags & MD_IM_SET_REPLICATED) {
if (imp_flags & META_IMP_REPORT) {
(void) printf("%i) %s:\n", set_count, gettext(
"Found partial replicated diskset "
"containing disks"));
} else {
(void) printf("\n%s:\n", gettext(
"Importing partial replicated diskset "
"containing disks"));
}
} else {
if (imp_flags & META_IMP_REPORT) {
(void) printf("%i) %s:\n", set_count, gettext(
"Found partial regular diskset containing "
"disks"));
} else {
(void) printf("\n%s:\n", gettext(
"Importing partial regular diskset "
"containing disks"));
}
}
} else {
if (misp->mis_flags & MD_IM_SET_REPLICATED) {
if (imp_flags & META_IMP_REPORT) {
(void) printf("%i) %s:\n", set_count, gettext(
"Found replicated diskset containing "
"disks"));
} else {
(void) printf("\n%s:\n", gettext(
"Importing replicated diskset containing "
"disks"));
}
} else {
if (imp_flags & META_IMP_REPORT) {
(void) printf("%i) %s:\n", set_count, gettext(
"Found regular diskset containing disks"));
} else {
(void) printf("\n%s:\n", gettext(
"Importing regular diskset containing "
"disks"));
}
}
}
/*
* Check each drive in the set. If it's unavailable or
* an overlap tell the user.
*/
for (d = misp->mis_drives; d != NULL; d = d->mid_next) {
(void) fprintf(stdout, " %s", d->mid_dnp->cname);
if (d->mid_available == MD_IM_DISK_NOT_AVAILABLE) {
(void) fprintf(stdout, " (UNAVAIL)");
}
if (overlap) {
md_im_drive_info_t **chain;
/*
* There is the potential for an overlap, see if
* this disk is one of the overlapped disks.
*/
for (chain = &overlap_disks; *chain != NULL;
chain = &(*chain)->overlap) {
if (strcmp(d->mid_dnp->cname,
(*chain)->mid_dnp->cname) == 0) {
(void) fprintf(stdout, " (CONFLICT)");
has_overlap = 1;
break;
}
}
}
(void) fprintf(stdout, "\n");
}
/*
* This note explains the (UNAVAIL) that appears next to the
* disks in the diskset that are not available.
*/
if (partial) {
(void) printf("%s%s\n%s%s\n\n", indent,
gettext("(UNAVAIL) WARNING: This disk is unavailable on"
" this system."), indent, gettext("Import may corrupt "
"data in the diskset."));
}
/*
* This note explains the (CONFLICT) that appears next to the
* disks whose lb_inittime timestamp does not
* match the rest of the diskset.
*/
if (has_overlap) {
(void) printf("%s%s\n%s%s\n\n", indent,
gettext("(CONFLICT) WARNING: This disk has been reused in "
"another diskset or system configuration."), indent,
gettext("Import may corrupt data in the diskset."));
}
/*
* If the verbose flag was given on the command line,
* we will print out the metastat -c information , the
* creation time, and last modified time for the diskset.
*/
if (imp_flags & META_IMP_VERBOSE) {
(void) printf("%s%s\n", indent,
gettext("Metadatabase information:"));
report_metadb_info(misp, indent);
/*
* Printing creation time and last modified time.
* Last modified: uses the global variable "lastaccess",
* which is set to the last updated timestamp from all of
* the database blocks(db_timestamp) or record blocks
* (rb_timestamp).
* Creation time is the locator block init time
* (lb_inittime).
*/
lastaccess = good_disk->mid_replicas->mir_timestamp;
(void) printf("%s%s\n", indent,
gettext("Metadevice information:"));
rval = report_metastat_info(mb, lbp, nm, pnm, rsp, fd,
&lastaccess, ep);
if (rval < 0) {
return (rval);
}
(void) printf("%s%s:\t%s\n", indent,
gettext("Creation time"),
meta_print_time(&good_disk->mid_replicas->mir_timestamp));
(void) printf("%s%s:\t%s\n", indent,
gettext("Last modified time"),
meta_print_time(&lastaccess));
} else {
/*
* Even if the verbose option is not set, we will print the
* creation time for the diskset.
*/
(void) printf("%s%s:\t%s\n", indent, gettext("Creation time"),
meta_print_time(&good_disk->mid_replicas->mir_timestamp));
}
/*
* If the diskset is not actually being imported, then we
* print out extra information about how to import it.
* If the verbose flag was not set, then we will also
* print out information about how to obtain verbose output.
*/
if (imp_flags & META_IMP_REPORT) {
/*
* TRANSLATION_NOTE
*
* The translation of the phrase "For more information
* about this set" will be followed by a ":" and a
* suggested command (untranslatable) that the user
* may use to request additional information.
*/
if (!(imp_flags & META_IMP_VERBOSE)) {
(void) printf("%s%s:\n%s %s -r -v %s\n", indent,
gettext("For more information about this diskset"),
indent, myname, good_disk->mid_dnp->cname);
}
if (meta_replica_quorum(misp) != 0)
no_quorum = 1;
/*
* TRANSLATION_NOTE
*
* The translation of the phrase "To import this set"
* will be followed by a ":" and a suggested command
* (untranslatable) that the user may use to import
* the specified diskset.
*/
if (partial || has_overlap || no_quorum) {
(void) printf("%s%s:\n%s %s -f -s <newsetname> %s\n",
indent, gettext("To import this diskset"), indent,
myname, good_disk->mid_dnp->cname);
} else {
(void) printf("%s%s:\n%s %s -s <newsetname> %s\n",
indent, gettext("To import this diskset"), indent,
myname, good_disk->mid_dnp->cname);
}
}
(void) printf("\n\n");
return (rval);
}
/*
* meta_get_and_report_set_info
*
* Scans a given drive for set specific information. If the given drive
* has a shared metadb, scans the shared metadb for information pertaining
* to the set.
* If imp_flags has META_IMP_PASS1 set don't report.
*
* Returns:
* <0 for failure
* 0 success but no replicas were found
* 1 success and a replica was found
*/
int
meta_get_and_report_set_info(
mddrivenamelist_t *dp,
md_im_set_desc_t **mispp,
int local_mb_ok,
uint_t imp_flags,
int *set_count,
int overlap,
md_im_drive_info_t *overlap_disks,
md_error_t *ep
)
{
uint_t s;
mdname_t *rsp;
int fd;
char mb[DEV_BSIZE];
/*LINTED*/
mddb_mb_t *mbp = (mddb_mb_t *)mb;
char lb[dbtob(MDDB_LBCNT)];
/*LINTED*/
mddb_lb_t *lbp = (mddb_lb_t *)lb;
mddb_did_blk_t *lbdidp = NULL;
mddb_ln_t *lnp = NULL;
int lnsize, lbdid_size;
int rval = 0;
char db[DEV_BSIZE];
/*LINTED*/
mddb_db_t *dbp = (mddb_db_t *)db;
did_list_t *did_listp = NULL;
mddrivenamelist_t *dnlp;
mddrivename_t *dnp;
md_im_names_t cnames = { 0, NULL};
char *nm = NULL, *shrnm = NULL;
char *did_nm = NULL, *did_shrnm = NULL;
struct nm_rec *nmp;
struct nm_shr_rec *snmp;
struct devid_shr_rec *did_shrnmp;
struct devid_min_rec *did_nmp;
int extended_namespace = 0;
int replicated = 0;
int partial = 0;
pnm_rec_t *pnm = NULL; /* list of physical devs in set */
md_im_set_desc_t *misp;
dnp = dp->drivenamep;
/*
* Determine and open the replica slice
*/
if (meta_replicaslice(dnp, &s, ep) != 0) {
return (-1);
}
/*
* Test for the size of replica slice in question. If
* the size is zero, we know that this is not a disk that was
* part of a set and it should be silently ignored for import.
*/
if (dnp->vtoc.parts[s].size == 0)
return (0);
if ((rsp = metaslicename(dnp, s, ep)) == NULL) {
return (-1);
}
if ((fd = open(rsp->rname, O_RDONLY|O_NDELAY)) < 0)
return (mdsyserror(ep, errno, rsp->cname));
/*
* After the open() succeeds, we should return via the "out"
* label to clean up after ourselves. (Up 'til now, we can
* just return directly, because there are no resources to
* give back.)
*/
if ((rval = read_master_block(ep, fd, mbp, sizeof (mb))) <= 0)
goto out;
replicated = is_replicated(fd, mbp, 0, NULL);
if (!local_mb_ok && mbp->mb_setno == 0) {
rval = 0;
goto out;
}
if ((rval = read_locator_block(ep, fd, mbp, lbp, sizeof (lb))) <= 0)
goto out;
/*
* Once the locator block has been read, we need to
* check if the locator block commit count is zero.
* If it is zero, we know that the replica we're dealing
* with is on a disk that was deleted from the disk set;
* and, it potentially has stale data. We need to quit
* in that case
*/
if (lbp->lb_commitcnt == 0) {
rval = 0;
goto out;
}
/*
* Make sure that the disk being imported has device id
* namespace present for disksets. If a disk doesn't have
* device id namespace, we skip reading the replica on that disk
*/
if (!(lbp->lb_flags & MDDB_DEVID_STYLE)) {
rval = 0;
goto out;
}
/*
* Grab the locator block device id array. Allocate memory for the
* array first.
*/
lbdid_size = dbtob(lbp->lb_didblkcnt);
lbdidp = Zalloc(lbdid_size);
if ((rval = read_locator_block_did(ep, fd, mbp, lbp, lbdidp,
lbdid_size)) <= 0)
goto out;
/*
* For a disk that has not been replicated, extract the device ids
* stored in the locator block device id array and store them in
* a list.
*
* If the disk has been replicated using replication software such
* as HDS Truecopy/ShadowImage or EMC SRDF/BCV, the device ids in
* the locator block are invalid and we need to build a list of
* replicated disks.
*/
if (imp_flags & META_IMP_PASS1) {
/*
* We need to do this for both passes but
* replicated_disk_list_built is global so we need some way
* to determine which pass we're on. Set it to the appropriate
* pass's flag.
*/
replicated_disk_list_built = &replicated_disk_list_built_pass1;
} else {
replicated_disk_list_built = &replicated_disk_list_built_pass2;
}
if (replicated && !(*replicated_disk_list_built)) {
/*
* if there's a replicated diskset involved, we need to
* scan the system one more time and build a list of all
* candidate disks that might be part of that replicated set
*/
if (meta_list_disks(ep, &cnames) != 0) {
rval = 0;
goto out;
}
dnlp = meta_prune_cnames(ep, &cnames, 0);
rval = build_replicated_disks_list(ep, dnlp);
if (rval == 0)
goto out;
}
/*
* Until here, we've gotten away with fixed sizes for the
* master block and locator block. The locator names,
* however, are sized (and therefore allocated) dynamically
* according to information in the locator block.
*/
lnsize = dbtob(lbp->lb_lnblkcnt);
lnp = Zalloc(lnsize);
if ((rval = read_locator_names(ep, fd, mbp, lbp, lnp, lnsize)) <= 0)
goto out;
rval = build_did_list(ep, fd, mbp, lbp, lbdidp, lnp, &did_listp,
replicated);
/*
* An rval of ENOTSUP means we have a partial diskset. We'll want
* to set the partial variable so we can pass this information
* set_append_wrapper later for placing on the misp list.
*/
if (rval == ENOTSUP)
partial = MD_IM_PARTIAL_DISKSET;
if (rval < 0)
goto out;
/*
* Read in the NM record
* If no NM record was found, it still is a valid configuration
* but it also means that we won't find any corresponding DID_NM
* or DID_SHR_NM.
*/
if ((rval = read_nm_rec(ep, fd, mbp, lbp, &nm, MDDB_NM, rsp->cname))
< 0)
goto out;
else if (rval == 0)
goto append;
/*
* At this point, we have read in all of the blocks that form
* the nm_rec. We should at least detect the corner case
* mentioned above, in which r_next_recid links to another
* nm_rec. Extended namespace handling is left for Phase 2.
*
* What this should really be is a loop, each iteration of
* which reads in a nm_rec and calls the set_append().
*/
/*LINTED*/
nmp = (struct nm_rec *)(nm + sizeof (mddb_rb_t));
if (nmp->r_rec_hdr.r_next_recid != (mddb_recid_t)0) {
extended_namespace = 1;
rval = 0;
goto out;
}
if ((rval = read_nm_rec(ep, fd, mbp, lbp, &shrnm, MDDB_SHR_NM,
rsp->cname)) < 0)
goto out;
else if (rval == 0)
goto append;
/*LINTED*/
snmp = (struct nm_shr_rec *)(shrnm + sizeof (mddb_rb_t));
if (snmp->sr_rec_hdr.r_next_recid != (mddb_recid_t)0) {
extended_namespace = 1;
rval = 0;
goto out;
}
if ((rval = read_nm_rec(ep, fd, mbp, lbp, &did_nm,
MDDB_DID_NM, rsp->cname)) < 0)
goto out;
else if (rval == 0)
goto append;
/*LINTED*/
did_nmp = (struct devid_min_rec *)(did_nm + sizeof (mddb_rb_t) -
sizeof (int));
if (did_nmp->min_rec_hdr.r_next_recid != (mddb_recid_t)0) {
extended_namespace = 1;
rval = 0;
goto out;
}
if ((rval = read_nm_rec(ep, fd, mbp, lbp, &did_shrnm,
MDDB_DID_SHR_NM, rsp->cname)) < 0)
goto out;
else if (rval == 0)
goto append;
/*LINTED*/
did_shrnmp = (struct devid_shr_rec *)(did_shrnm + sizeof (mddb_rb_t) -
sizeof (int));
if (did_shrnmp->did_rec_hdr.r_next_recid != (mddb_recid_t)0) {
extended_namespace = 1;
rval = 0;
goto out;
}
/*
* We need to check if all of the disks listed in the namespace
* are actually available. If they aren't we'll return with
* an ENOTSUP error which indicates a partial diskset.
*/
rval = check_nm_disks(did_nmp, did_shrnmp);
/*
* An rval of ENOTSUP means we have a partial diskset. We'll want
* to set the partial variable so we can pass this information
* to set_append_wrapper later for placing on the misp list.
*/
if (rval == ENOTSUP)
partial = MD_IM_PARTIAL_DISKSET;
if (rval < 0)
goto out;
append:
/* Finally, we've got what we need to process this replica. */
misp = set_append(mispp, did_listp, mbp, lbp,
/*LINTED*/
(mddb_rb_t *)nm, (mddb_rb_t *)shrnm, &pnm, (mddb_rb_t *)did_nm,
/*LINTED*/
(mddb_rb_t *)did_shrnm, (imp_flags | partial | replicated), ep);
if (!(imp_flags & META_IMP_PASS1)) {
*set_count += 1;
rval = report_set_info(misp, mbp, lbp,
/*LINTED*/
(mddb_rb_t *)nm, &pnm, rsp, fd, imp_flags, *set_count,
overlap, overlap_disks, ep);
if (rval < 0)
goto out;
}
/* Return the fact that we found at least one set */
rval = 1;
out:
if (fd >= 0)
(void) close(fd);
if (did_listp != NULL)
free_did_list(did_listp);
if (lnp != NULL)
Free(lnp);
if (nm != NULL)
Free(nm);
if (did_nm != NULL)
Free(did_nm);
if (did_shrnm != NULL)
Free(did_shrnm);
if (pnm != NULL)
free_pnm_rec_list(&pnm);
/*
* If we are at the end of the list, we must free up
* the replicated list too
*/
if (dp->next == NULL)
free_replicated_disks_list();
if (extended_namespace)
return (mddserror(ep, MDE_DS_EXTENDEDNM, MD_SET_BAD,
mynode(), NULL, NULL));
return (rval);
}
/*
* Return the minor name associated with a given disk slice
*/
static char *
meta_getminor_name(
char *devname,
md_error_t *ep
)
{
int fd = -1;
char *minor_name = NULL;
char *ret_minor_name = NULL;
if (devname == NULL)
return (NULL);
if ((fd = open(devname, O_RDONLY|O_NDELAY, 0)) < 0) {
(void) mdsyserror(ep, errno, devname);
return (NULL);
}
if (devid_get_minor_name(fd, &minor_name) == 0) {
ret_minor_name = Strdup(minor_name);
devid_str_free(minor_name);
}
(void) close(fd);
return (ret_minor_name);
}
/*
* meta_update_mb_did
*
* Update or create the master block with the new set number.
* If a non-null devid pointer is given, the devid in the
* master block will also be changed.
*
* This routine is called during the import of a diskset
* (meta_imp_update_mb) and during the take of a diskset that has
* some unresolved replicated drives (meta_unrslv_replicated_mb).
*
* Returns : nothing (void)
*/
static void
meta_update_mb_did(
mdsetname_t *sp,
mddrivename_t *dnp, /* raw name of drive with mb */
void *new_devid, /* devid to be stored in mb */
int new_devid_len,
void *old_devid, /* old devid stored in mb */
int replica_present, /* does replica follow mb? */
int offset,
md_error_t *ep
)
{
int fd;
struct mddb_mb *mbp;
uint_t sliceno;
mdname_t *rsp;
/* determine the replica slice */
if (meta_replicaslice(dnp, &sliceno, ep) != 0) {
return;
}
/*
* if the replica slice size is zero,
* don't bother opening
*/
if (dnp->vtoc.parts[sliceno].size == 0) {
return;
}
if ((rsp = metaslicename(dnp, sliceno, ep)) == NULL) {
return;
}
if ((fd = open(rsp->rname, O_RDWR | O_NDELAY)) < 0) {
return;
}
if (lseek(fd, (off_t)dbtob(offset), SEEK_SET) < 0)
return;
mbp = Zalloc(DEV_BSIZE);
if (read(fd, mbp, DEV_BSIZE) != DEV_BSIZE) {
Free(mbp);
return;
}
/* If no replica on disk, check for dummy mb */
if (replica_present == NULL) {
/*
* Check to see if there is a dummy there. If not
* create one. This would happen if the set was
* created before the master block dummy code was
* implemented.
*/
if ((mbp->mb_magic != MDDB_MAGIC_DU) ||
(mbp->mb_revision != MDDB_REV_MB)) {
meta_mkdummymaster(sp, fd, offset);
Free(mbp);
return;
}
}
mbp->mb_setno = sp->setno;
if (meta_gettimeofday(&mbp->mb_timestamp) == -1) {
Free(mbp);
return;
}
/*
* If a old_devid is non-NULL then we're are dealing with a
* replicated diskset and the devid needs to be updated.
*/
if (old_devid) {
if (mbp->mb_devid_magic == MDDB_MAGIC_DE) {
if (mbp->mb_devid_len)
(void) memset(mbp->mb_devid, 0,
mbp->mb_devid_len);
(void) memcpy(mbp->mb_devid,
(char *)new_devid, new_devid_len);
mbp->mb_devid_len = new_devid_len;
}
}
crcgen((uchar_t *)mbp, (uint_t *)&mbp->mb_checksum,
(uint_t)DEV_BSIZE, (crc_skip_t *)NULL);
/*
* Now write out the changes to disk.
* If an error occurs, just continue on.
* Next take of set will register this drive as
* an unresolved replicated drive and will attempt
* to fix the master block again.
*/
if (lseek(fd, (off_t)dbtob(offset), SEEK_SET) < 0) {
Free(mbp);
return;
}
if (write(fd, mbp, DEV_BSIZE) != DEV_BSIZE) {
Free(mbp);
return;
}
Free(mbp);
(void) close(fd);
}
/*
* meta_imp_update_mb
*
* Update the master block information during an import.
* Takes an import set descriptor.
*
* Returns : nothing (void)
*/
void
meta_imp_update_mb(mdsetname_t *sp, md_im_set_desc_t *misp, md_error_t *ep)
{
md_im_drive_info_t *midp;
mddrivename_t *dnp;
int offset = 16; /* default mb offset is 16 */
for (midp = misp->mis_drives; midp != NULL; midp = midp->mid_next) {
/*
* If disk isn't available we can't update, so go to next
*/
if (midp->mid_available == MD_IM_DISK_NOT_AVAILABLE) {
continue;
}
dnp = midp->mid_dnp;
if (midp->mid_replicas) {
md_im_replica_info_t *mirp;
/*
* If we have replicas on this disk we need to make
* sure that we update the master block on every
* replica on the disk.
*/
for (mirp = midp->mid_replicas; mirp != NULL;
mirp = mirp->mir_next) {
offset = mirp->mir_offset;
meta_update_mb_did(sp, dnp, midp->mid_devid,
midp->mid_devid_sz, midp->mid_o_devid,
1, offset, ep);
}
} else {
/* No replicas, just update the one dummy mb */
meta_update_mb_did(sp, dnp, midp->mid_devid,
midp->mid_devid_sz, midp->mid_o_devid,
0, offset, ep);
}
if (!mdisok(ep))
return;
}
}
/*
* meta_unrslv_replicated_common
*
* Given a drive_desc and a drivenamelist pointer,
* return the devidp associated with the drive_desc,
* the replicated (new) devidp associated with the drive_desc
* and the specific mddrivename in the drivenamelist that
* matches the replicated (new) devidp.
*
* Typically the drivenamelist pointer would be setup by
* the meta_prune_cnames function.
*
* Calling function must free devidp using devid_free.
*
* Returns 0 - success, found new_devidp and dnp_new.
* Returns 1 - failure, didn't find new devid info
*/
static int
meta_unrslv_replicated_common(
int myside,
md_drive_desc *dd, /* drive list for diskset */
mddrivenamelist_t *dnlp, /* list of drives on current system */
ddi_devid_t *devidp, /* old devid */
ddi_devid_t *new_devidp, /* replicated (new) devid */
mddrivename_t **dnp_new, /* replicated drive name */
md_error_t *ep
)
{
mddrivename_t *dnp; /* drive name of old drive */
mdsidenames_t *sn = NULL;
uint_t rep_slice;
mdname_t *np;
char *minor_name = NULL;
char *devid_str = NULL;
size_t len;
int devid_sz;
mddrivenamelist_t *dp;
ddi_devid_t old_devid; /* devid of old drive */
ddi_devid_t new_devid; /* devid of new replicated drive */
ddi_devid_t dnp_new_devid; /* devid derived from drive */
/* name of replicated drive */
dnp = dd->dd_dnp;
/* Get old devid from drive record */
(void) devid_str_decode(dd->dd_dnp->devid,
&old_devid, NULL);
/* Look up replicated (new) devid */
new_devid = replicated_list_lookup(
devid_sizeof(old_devid), old_devid);
devid_free(old_devid);
if (new_devid == NULL)
return (1);
/*
* Using new_devid, find a drivename entry with a matching devid.
* Use the passed in dnlp since it has the new (replicated) disknames
* in it.
*/
for (dp = dnlp; dp != NULL; dp = dp->next) {
(void) devid_str_decode(dp->drivenamep->devid,
&dnp_new_devid, NULL);
if (dnp_new_devid == NULL)
continue;
if (devid_compare(new_devid, dnp_new_devid) == 0) {
devid_free(dnp_new_devid);
break;
}
devid_free(dnp_new_devid);
}
/* If can't find new name for drive - nothing to update */
if (dp == NULL)
return (1);
/*
* Setup returned value to be the drivename structure associated
* with new (replicated) drive.
*/
*dnp_new = dp->drivenamep;
/*
* Need to return the new devid including the minor name.
* Find the minor_name here using the sidename or by
* looking in the namespace.
*/
for (sn = dnp->side_names; sn != NULL; sn = sn->next) {
if (sn->sideno == myside)
break;
}
/*
* The disk has no side name information
*/
if (sn == NULL) {
if ((meta_replicaslice(*dnp_new, &rep_slice, ep) != 0) ||
((np = metaslicename(*dnp_new, rep_slice, ep))
== NULL)) {
mdclrerror(ep);
return (1);
}
if (np->dev == NODEV64)
return (1);
/*
* minor_name will be NULL if dnp->devid == NULL
* - see metagetvtoc()
*/
if (np->minor_name == NULL)
return (1);
else
minor_name = Strdup(np->minor_name);
} else {
minor_name = meta_getdidminorbykey(
MD_LOCAL_SET, sn->sideno + SKEW,
dnp->side_names_key, ep);
if (!mdisok(ep))
return (1);
}
/*
* Now, use the old devid with minor name to lookup
* the replicated (new) devid that will also contain
* a minor name.
*/
len = strlen(dnp->devid) + strlen(minor_name) + 2;
devid_str = (char *)Malloc(len);
(void) snprintf(devid_str, len, "%s/%s", dnp->devid,
minor_name);
(void) devid_str_decode(devid_str, devidp, NULL);
Free(devid_str);
devid_sz = devid_sizeof((ddi_devid_t)*devidp);
*new_devidp = replicated_list_lookup(devid_sz, *devidp);
return (0);
}
/*
* meta_unrslv_replicated_mb
*
* Update the master block information during a take.
* Takes an md_drive_desc descriptor.
*
* Returns : nothing (void)
*/
void
meta_unrslv_replicated_mb(
mdsetname_t *sp,
md_drive_desc *dd, /* drive list for diskset */
mddrivenamelist_t *dnlp, /* list of drives on current system */
md_error_t *ep
)
{
md_drive_desc *d = NULL, *d_save;
mddrivename_t *dnp; /* dnp of old drive */
mddrivename_t *dnp_new; /* dnp of new (replicated) drive */
mddrivename_t *dnp_save; /* saved copy needed to restore */
ddi_devid_t devidp, new_devidp;
int myside;
if ((myside = getmyside(sp, ep)) == MD_SIDEWILD)
return;
for (d = dd; d != NULL; d = d->dd_next) {
dnp = d->dd_dnp;
if (dnp == NULL)
continue;
/* If don't need to update master block - skip it. */
if (!(d->dd_flags & MD_DR_FIX_MB_DID))
continue;
/*
* Get old and replicated (new) devids associated with this
* drive. Also, get the new (replicated) drivename structure.
*/
if (meta_unrslv_replicated_common(myside, d, dnlp, &devidp,
&new_devidp, &dnp_new, ep) != 0) {
mdclrerror(ep);
continue;
}
if (new_devidp) {
int offset = 16; /* default mb offset is 16 */
int dbcnt;
if (d->dd_dbcnt) {
/*
* Update each master block on the disk
*/
for (dbcnt = d->dd_dbcnt; dbcnt != 0; dbcnt--) {
meta_update_mb_did(sp, dnp_new,
new_devidp,
devid_sizeof(new_devidp), devidp,
1, offset, ep);
offset += d->dd_dbsize;
}
} else {
/* update the one dummy mb */
meta_update_mb_did(sp, dnp_new, new_devidp,
devid_sizeof(new_devidp), devidp,
0, offset, ep);
}
if (!mdisok(ep)) {
devid_free(devidp);
return;
}
/* Set drive record flags to ok */
/* Just update this one drive record. */
d_save = d->dd_next;
dnp_save = d->dd_dnp;
d->dd_next = NULL;
d->dd_dnp = dnp_new;
/* Ignore failure since no bad effect. */
(void) clnt_upd_dr_flags(mynode(), sp, d,
MD_DR_OK, ep);
d->dd_next = d_save;
d->dd_dnp = dnp_save;
}
devid_free(devidp);
}
}
/*
* meta_update_nm_rr_did
*
* Change a devid stored in the diskset namespace and in the local set
* namespace with the new devid.
*
* This routine is called during the import of a diskset
* (meta_imp_update_nn) and during the take of a diskset that has
* some unresolved replicated drives (meta_unrslv_replicated_nm).
*
* Returns : nothing (void)
*/
static void
meta_update_nm_rr_did(
mdsetname_t *sp,
void *old_devid, /* old devid being replaced */
int old_devid_sz,
void *new_devid, /* devid to be stored in nm */
int new_devid_sz,
int import_flag, /* called during import? */
md_error_t *ep
)
{
struct mddb_config c;
(void) memset(&c, 0, sizeof (c));
c.c_setno = sp->setno;
/* During import to NOT update the local namespace. */
if (import_flag)
c.c_flags = MDDB_C_IMPORT;
c.c_locator.l_devid = (uintptr_t)Malloc(new_devid_sz);
(void) memcpy((void *)(uintptr_t)c.c_locator.l_devid,
new_devid, new_devid_sz);
c.c_locator.l_devid_sz = new_devid_sz;
c.c_locator.l_devid_flags =
MDDB_DEVID_VALID | MDDB_DEVID_SPACE | MDDB_DEVID_SZ;
c.c_locator.l_old_devid = (uint64_t)(uintptr_t)Malloc(old_devid_sz);
(void) memcpy((void *)(uintptr_t)c.c_locator.l_old_devid,
old_devid, old_devid_sz);
c.c_locator.l_old_devid_sz = old_devid_sz;
if (metaioctl(MD_IOCUPDATE_NM_RR_DID, &c, &c.c_mde, NULL) != 0) {
(void) mdstealerror(ep, &c.c_mde);
}
Free((void *)(uintptr_t)c.c_locator.l_devid);
Free((void *)(uintptr_t)c.c_locator.l_old_devid);
}
/*
* meta_imp_update_nm
*
* Change a devid stored in the diskset namespace with the new devid.
* This routine is called during the import of a remotely replicated diskset.
*
* Returns : nothing (void)
*/
void
meta_imp_update_nm(mdsetname_t *sp, md_im_set_desc_t *misp, md_error_t *ep)
{
md_im_drive_info_t *midp;
for (midp = misp->mis_drives; midp != NULL; midp = midp->mid_next) {
/*
* If disk isn't available we can't update, so go to next
*/
if (midp->mid_available == MD_IM_DISK_NOT_AVAILABLE) {
continue;
}
meta_update_nm_rr_did(sp, midp->mid_o_devid,
midp->mid_o_devid_sz, midp->mid_devid,
midp->mid_devid_sz, 1, ep);
if (!mdisok(ep))
return;
}
}
/*
* meta_unrslv_replicated_nm
*
* Change a devid stored in the diskset namespace and in the local set
* namespace with the new devid.
*
* This routine is called during the take of a diskset that has
* some unresolved replicated drives.
*
* Returns : nothing (void)
*/
void
meta_unrslv_replicated_nm(
mdsetname_t *sp,
md_drive_desc *dd, /* drive list for diskset */
mddrivenamelist_t *dnlp, /* list of drives on current system */
md_error_t *ep
)
{
md_drive_desc *d = NULL;
mddrivename_t *dnp; /* drive name of old drive */
mddrivename_t *dnp_new; /* drive name of new (repl) drive */
ddi_devid_t devidp, new_devidp;
ddi_devid_t old_devid;
char *devid_old_save;
mdsetname_t *local_sp = NULL;
int myside;
if ((myside = getmyside(sp, ep)) == MD_SIDEWILD)
return;
for (d = dd; d != NULL; d = d->dd_next) {
dnp = d->dd_dnp;
if (dnp == NULL)
continue;
/* If don't need to update namespace - skip it. */
if (!(d->dd_flags & MD_DR_FIX_LB_NM_DID))
continue;
/* Get old devid from drive record */
(void) devid_str_decode(d->dd_dnp->devid,
&old_devid, NULL);
/*
* Get old and replicated (new) devids associated with this
* drive. Also, get the new (replicated) drivename structure.
*/
if (meta_unrslv_replicated_common(myside, d, dnlp, &devidp,
&new_devidp, &dnp_new, ep) != 0) {
mdclrerror(ep);
continue;
}
if (new_devidp) {
meta_update_nm_rr_did(sp, devidp,
devid_sizeof(devidp), new_devidp,
devid_sizeof(new_devidp), 0, ep);
if (!mdisok(ep)) {
devid_free(devidp);
return;
}
}
devid_free(devidp);
/*
* Using the new devid, fix up the name.
* If meta_upd_ctdnames fails, the next take will re-resolve
* the name from the new devid.
*/
local_sp = metasetname(MD_LOCAL_NAME, ep);
devid_old_save = dnp->devid;
dnp->devid = dnp_new->devid;
(void) meta_upd_ctdnames(&local_sp, 0, (myside + SKEW),
dnp, NULL, ep);
mdclrerror(ep);
dnp->devid = devid_old_save;
}
}
static set_t
meta_imp_setno(
md_error_t *ep
)
{
set_t max_sets, setno;
int bool;
if ((max_sets = get_max_sets(ep)) == 0) {
return (MD_SET_BAD);
}
/*
* This code needs to be expanded when we run in SunCluster
* environment SunCluster obtains setno internally
*/
for (setno = 1; setno < max_sets; setno++) {
if (clnt_setnumbusy(mynode(), setno,
&bool, ep) == -1) {
setno = MD_SET_BAD;
break;
}
/*
* found one available
*/
if (bool == FALSE)
break;
}
if (setno == max_sets) {
setno = MD_SET_BAD;
}
return (setno);
}
int
meta_imp_set(
md_im_set_desc_t *misp,
char *setname,
int force,
bool_t dry_run,
md_error_t *ep
)
{
md_timeval32_t tp;
md_im_drive_info_t *midp;
uint_t rep_slice;
mddrivename_t *dnp;
struct mddb_config c;
mdname_t *np;
md_im_replica_info_t *mirp;
set_t setno;
mdcinfo_t *cinfo;
mdsetname_t *sp;
mddrivenamelist_t *dnlp = NULL;
mddrivenamelist_t **dnlpp = &dnlp;
char *minor_name = NULL;
int stale_flag = 0;
md_set_desc *sd;
int partial_replicated_flag = 0;
md_error_t xep = mdnullerror;
md_setkey_t *cl_sk;
(void) memset(&c, 0, sizeof (c));
(void) strlcpy(c.c_setname, setname, sizeof (c.c_setname));
c.c_sideno = 0;
c.c_flags = MDDB_C_IMPORT;
/*
* Check to see if the setname that the set is being imported into,
* already exists.
*/
if (getsetbyname(c.c_setname, ep) != NULL) {
return (mddserror(ep, MDE_DS_SETNAMEBUSY, MD_SET_BAD,
mynode(), NULL, c.c_setname));
}
/*
* Find the next available set number
*/
if ((setno = meta_imp_setno(ep)) == MD_SET_BAD) {
return (mddserror(ep, MDE_DS_SETNOTIMP, MD_SET_BAD,
mynode(), NULL, c.c_setname));
}
c.c_setno = setno;
if (meta_gettimeofday(&tp) == -1) {
return (mdsyserror(ep, errno, NULL));
}
c.c_timestamp = tp;
/* Check to see if replica quorum requirement is fulfilled */
if (meta_replica_quorum(misp) == -1) {
if (!force) {
return (mddserror(ep, MDE_DS_INSUFQUORUM, MD_SET_BAD,
mynode(), NULL, c.c_setname));
} else {
stale_flag = MD_IMP_STALE_SET;
/*
* If we have a stale diskset, the kernel will
* delete the replicas on the unavailable disks.
* To be consistent, we'll zero out the mirp on those
* disks here.
*/
for (midp = misp->mis_drives; midp != NULL;
midp = midp->mid_next) {
if (midp->mid_available ==
MD_IM_DISK_NOT_AVAILABLE) {
midp->mid_replicas = NULL;
}
}
}
}
for (midp = misp->mis_drives; midp != NULL;
midp = midp->mid_next) {
if ((misp->mis_flags & MD_IM_SET_REPLICATED) &&
(partial_replicated_flag == 0) &&
(midp->mid_available == MD_IM_DISK_NOT_AVAILABLE))
partial_replicated_flag = MD_SR_UNRSLV_REPLICATED;
/*
* We pass the list of the drives in the
* set with replicas on them down to the kernel.
*/
dnp = midp->mid_dnp;
mirp = midp->mid_replicas;
if (!mirp) {
/*
* No replicas on this disk, go to next disk.
*/
continue;
}
if (midp->mid_available == MD_IM_DISK_NOT_AVAILABLE) {
/*
* The disk isn't there. We'll need to get the
* disk information from the midp list instead
* of going and looking for it. This means it
* will be information relative to the old
* system.
*/
minor_name = Strdup(midp->mid_minor_name);
(void) strncpy(c.c_locator.l_driver,
midp->mid_driver_name,
sizeof (c.c_locator.l_driver));
(void) strcpy(c.c_locator.l_devname, midp->mid_devname);
c.c_locator.l_mnum = midp->mid_mnum;
} else {
if ((meta_replicaslice(dnp, &rep_slice, ep) != 0) ||
((np = metaslicename(dnp, rep_slice, ep))
== NULL)) {
mdclrerror(ep);
continue;
}
(void) strcpy(c.c_locator.l_devname, np->bname);
c.c_locator.l_dev = meta_cmpldev(np->dev);
c.c_locator.l_mnum = meta_getminor(np->dev);
minor_name = meta_getminor_name(np->bname, ep);
if ((cinfo = metagetcinfo(np, ep)) == NULL) {
mdclrerror(ep);
continue;
}
if (cinfo->dname) {
(void) strncpy(c.c_locator.l_driver,
cinfo->dname,
sizeof (c.c_locator.l_driver));
}
}
c.c_locator.l_devid = (uintptr_t)Malloc(midp->mid_devid_sz);
(void) memcpy((void *)(uintptr_t)c.c_locator.l_devid,
midp->mid_devid, midp->mid_devid_sz);
c.c_locator.l_devid_sz = midp->mid_devid_sz;
c.c_locator.l_devid_flags =
MDDB_DEVID_VALID | MDDB_DEVID_SPACE | MDDB_DEVID_SZ;
if (midp->mid_o_devid) {
c.c_locator.l_old_devid =
(uint64_t)(uintptr_t)Malloc(midp->mid_o_devid_sz);
(void) memcpy((void *)(uintptr_t)
c.c_locator.l_old_devid,
midp->mid_o_devid, midp->mid_o_devid_sz);
c.c_locator.l_old_devid_sz = midp->mid_o_devid_sz;
}
if (minor_name) {
(void) strncpy(c.c_locator.l_minor_name, minor_name,
sizeof (c.c_locator.l_minor_name));
}
do {
c.c_locator.l_flags = 0;
c.c_locator.l_blkno = mirp->mir_offset;
if (metaioctl(MD_DB_USEDEV, &c, &c.c_mde, NULL) != 0) {
Free((void *)(uintptr_t)c.c_locator.l_devid);
if (c.c_locator.l_old_devid)
Free((void *)(uintptr_t)
c.c_locator.l_old_devid);
return (mdstealerror(ep, &c.c_mde));
}
mirp = mirp->mir_next;
} while (mirp != NULL);
}
/*
* If the dry run option was specified, flag success
* and exit out
*/
if (dry_run == 1) {
md_eprintf("%s\n", dgettext(TEXT_DOMAIN,
"import should be successful"));
Free((void *)(uintptr_t)c.c_locator.l_devid);
if (c.c_locator.l_old_devid)
Free((void *)(uintptr_t)c.c_locator.l_old_devid);
return (0);
}
/*
* Now the kernel should have all the information
* regarding the import diskset replica.
* Tell the kernel to load them up and import the set
*/
(void) memset(&c, 0, sizeof (c));
c.c_flags = stale_flag;
c.c_setno = setno;
if (metaioctl(MD_IOCIMP_LOAD, &c, &c.c_mde, NULL) != 0) {
Free((void *)(uintptr_t)c.c_locator.l_devid);
if (c.c_locator.l_old_devid)
Free((void *)(uintptr_t)c.c_locator.l_old_devid);
return (mdstealerror(ep, &c.c_mde));
}
(void) meta_smf_enable(META_SMF_DISKSET, NULL);
/*
* Create a set name for the set.
*/
sp = Zalloc(sizeof (*sp));
sp->setname = Strdup(setname);
sp->lockfd = MD_NO_LOCK;
sp->setno = setno;
sd = Zalloc(sizeof (*sd));
(void) strcpy(sd->sd_nodes[0], mynode());
sd->sd_ctime = tp;
sd->sd_genid = 0;
if (misp->mis_flags & MD_IM_SET_REPLICATED) {
/* Update the diskset namespace */
meta_imp_update_nm(sp, misp, ep);
/* Release the diskset - even if update_nm failed */
(void) memset(&c, 0, sizeof (c));
c.c_setno = setno;
/* Don't need device id information from this ioctl */
c.c_locator.l_devid = (uint64_t)0;
c.c_locator.l_devid_flags = 0;
if (metaioctl(MD_RELEASE_SET, &c, &c.c_mde, NULL) != 0) {
if (mdisok(ep))
(void) mdstealerror(ep, &c.c_mde);
Free(sd);
Free(sp);
return (-1);
}
/* If update_nm failed, then fail the import. */
if (!mdisok(ep)) {
Free(sd);
Free(sp);
return (-1);
}
}
/*
* We'll need to update information in the master block due
* to the set number changing and if the case of a replicated
* diskset, the device id changing. May also need to create a
* dummy master block if it's not there.
*/
meta_imp_update_mb(sp, misp, ep);
if (!mdisok(ep)) {
Free(sd);
Free(sp);
return (-1);
}
/*
* Create set record for diskset, but record is left in
* MD_SR_ADD state until after drives are added to set.
*/
if (clnt_lock_set(mynode(), sp, ep)) {
Free(sd);
Free(sp);
return (-1);
}
if (clnt_createset(mynode(), sp, sd->sd_nodes,
sd->sd_ctime, sd->sd_genid, ep)) {
cl_sk = cl_get_setkey(sp->setno, sp->setname);
(void) clnt_unlock_set(mynode(), cl_sk, &xep);
Free(sd);
Free(sp);
return (-1);
}
Free(sd);
/*
* Create drive records for the disks in the set.
*/
for (midp = misp->mis_drives; midp != NULL; midp = midp->mid_next) {
dnp = midp->mid_dnp;
if (midp->mid_available & MD_IM_DISK_NOT_AVAILABLE) {
/*
* If the disk isn't available, the dnp->devid is
* no good. It is either blank for the case where
* there is no disk with that devname, or it
* contains the devid for the real disk in the system
* with that name. The problem is, if the disk is
* unavailable, then the devid should be the devid
* of the missing disk. So we're faking a dnp for
* the import. This is needed for creating drive
* records.
*/
dnp = Zalloc(sizeof (mddrivename_t));
dnp->side_names_key = midp->mid_dnp->side_names_key;
dnp->type = midp->mid_dnp->type;
dnp->cname = Strdup(midp->mid_dnp->cname);
dnp->rname = Strdup(midp->mid_dnp->rname);
dnp->devid = devid_str_encode(midp->mid_devid,
NULL);
midp->mid_dnp = dnp;
}
dnlpp = meta_drivenamelist_append_wrapper(dnlpp, dnp);
}
if (meta_imp_set_adddrives(sp, dnlp, misp, ep)) {
Free(sp);
return (mddserror(ep, MDE_DS_SETNOTIMP, MD_SET_BAD,
mynode(), NULL, c.c_setname));
}
/* If drives were added without error, set set_record to OK */
if (clnt_upd_sr_flags(mynode(), sp,
(partial_replicated_flag | MD_SR_OK | MD_SR_MB_DEVID), ep)) {
Free(sp);
return (mddserror(ep, MDE_DS_SETNOTIMP, MD_SET_BAD,
mynode(), NULL, c.c_setname));
}
Free(sp);
cl_sk = cl_get_setkey(sp->setno, sp->setname);
if (clnt_unlock_set(mynode(), cl_sk, ep)) {
return (-1);
}
cl_set_setkey(NULL);
Free((void *)(uintptr_t)c.c_locator.l_devid);
if (c.c_locator.l_old_devid)
Free((void *)(uintptr_t)c.c_locator.l_old_devid);
return (0);
}