2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 1993, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <dlfcn.h>
2N/A#include <meta.h>
2N/A#include <metadyn.h>
2N/A#include <ctype.h>
2N/A#include <dirent.h>
2N/A#include <devid.h>
2N/A#include <sys/param.h>
2N/A#include <sys/scsi/impl/uscsi.h>
2N/A#include <sys/scsi/generic/commands.h>
2N/A#include <sys/scsi/generic/inquiry.h>
2N/A#include <sys/efi_partition.h>
2N/A
2N/Atypedef struct ctlr_cache {
2N/A char *ctlr_nm;
2N/A int ctlr_ty;
2N/A struct ctlr_cache *ctlr_nx;
2N/A} ctlr_cache_t;
2N/A
2N/Astatic ctlr_cache_t *ctlr_cache = NULL;
2N/A
2N/A
2N/A/*
2N/A * return set for a device
2N/A */
2N/Amdsetname_t *
2N/Ametagetset(
2N/A mdname_t *np,
2N/A int bypass_daemon,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdsetname_t *sp;
2N/A
2N/A /* metadevice */
2N/A if (metaismeta(np))
2N/A return (metasetnosetname(MD_MIN2SET(meta_getminor(np->dev)),
2N/A ep));
2N/A
2N/A /* regular device */
2N/A if (meta_is_drive_in_anyset(np->drivenamep, &sp, bypass_daemon,
2N/A ep) != 0)
2N/A return (NULL);
2N/A
2N/A if (sp != NULL)
2N/A return (sp);
2N/A
2N/A return (metasetnosetname(MD_LOCAL_SET, ep));
2N/A}
2N/A
2N/A/*
2N/A * convert system to md types
2N/A */
2N/Astatic void
2N/Ameta_geom_to_md(
2N/A struct dk_geom *gp,
2N/A mdgeom_t *mdgp
2N/A)
2N/A{
2N/A (void) memset(mdgp, '\0', sizeof (*mdgp));
2N/A mdgp->ncyl = gp->dkg_ncyl;
2N/A mdgp->nhead = gp->dkg_nhead;
2N/A mdgp->nsect = gp->dkg_nsect;
2N/A mdgp->rpm = gp->dkg_rpm;
2N/A mdgp->write_reinstruct = gp->dkg_write_reinstruct;
2N/A mdgp->read_reinstruct = gp->dkg_read_reinstruct;
2N/A mdgp->blk_sz = DEV_BSIZE;
2N/A}
2N/A
2N/A/*
2N/A * convert efi to md types
2N/A */
2N/Astatic void
2N/Ameta_efi_to_mdgeom(md_unit_t *mdev, struct dk_gpt *gpt, mdgeom_t *mdgp)
2N/A{
2N/A (void) memset(mdgp, '\0', sizeof (*mdgp));
2N/A
2N/A /*
2N/A * Should always get geom from metadevice unit if metadevice.
2N/A * If metadevice is built on top of efi disks then it will
2N/A * have MD_EFI_FG_ values, otherwise it will have geom from
2N/A * the first component.
2N/A */
2N/A if (mdev) {
2N/A mdgp->ncyl = (mdev->c.un_total_blocks) /
2N/A (mdev->c.un_nhead * mdev->c.un_nsect);
2N/A mdgp->nhead = mdev->c.un_nhead;
2N/A mdgp->nsect = mdev->c.un_nsect;
2N/A } else {
2N/A mdgp->ncyl = (gpt->efi_last_u_lba - gpt->efi_first_u_lba)
2N/A / (MD_EFI_FG_HEADS * MD_EFI_FG_SECTORS);
2N/A mdgp->nhead = MD_EFI_FG_HEADS;
2N/A mdgp->nsect = MD_EFI_FG_SECTORS;
2N/A }
2N/A
2N/A mdgp->rpm = MD_EFI_FG_RPM;
2N/A mdgp->write_reinstruct = MD_EFI_FG_WRI;
2N/A mdgp->read_reinstruct = MD_EFI_FG_RRI;
2N/A mdgp->blk_sz = DEV_BSIZE;
2N/A}
2N/A
2N/Astatic void
2N/Ameta_efi_to_mdvtoc(struct dk_gpt *gpt, mdvtoc_t *mdvp)
2N/A{
2N/A char typename[EFI_PART_NAME_LEN];
2N/A uint_t i;
2N/A
2N/A (void) memset(mdvp, '\0', sizeof (*mdvp));
2N/A mdvp->nparts = gpt->efi_nparts;
2N/A if (mdvp->nparts > MD_MAX_PARTS)
2N/A return;
2N/A
2N/A mdvp->first_lba = gpt->efi_first_u_lba;
2N/A mdvp->last_lba = gpt->efi_last_u_lba;
2N/A mdvp->lbasize = gpt->efi_lbasize;
2N/A
2N/A for (i = 0; (i < gpt->efi_nparts); ++i) {
2N/A mdvp->parts[i].start = gpt->efi_parts[i].p_start;
2N/A mdvp->parts[i].size = gpt->efi_parts[i].p_size;
2N/A mdvp->parts[i].tag = gpt->efi_parts[i].p_tag;
2N/A mdvp->parts[i].flag = gpt->efi_parts[i].p_flag;
2N/A /*
2N/A * It is possible to present an efi label but be using vtoc
2N/A * disks to create a > 1 TB metadevice. In case the first
2N/A * disk in the underlying metadevice is a vtoc disk and starts
2N/A * at the beginning of the disk it is necessary to convey this
2N/A * information to the user.
2N/A */
2N/A if (mdvp->parts[i].size > 0 &&
2N/A mdvp->parts[i].start != 0 && mdvp->nparts == 1) {
2N/A mdvp->parts[i].label = btodb(DK_LABEL_SIZE);
2N/A mdvp->parts[i].start = 0;
2N/A }
2N/A
2N/A /*
2N/A * Due to the lack of a label for the entire partition table,
2N/A * we use p_name of the reserved partition
2N/A */
2N/A if ((gpt->efi_parts[i].p_tag == V_RESERVED) &&
2N/A (gpt->efi_parts[i].p_name != NULL)) {
2N/A (void) strlcpy(typename, gpt->efi_parts[i].p_name,
2N/A EFI_PART_NAME_LEN);
2N/A /* Stop at first (if any) space or tab */
2N/A (void) strtok(typename, " \t");
2N/A mdvp->typename = Strdup(typename);
2N/A }
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Ameta_mdvtoc_to_efi(mdvtoc_t *mdvp, struct dk_gpt **gpt)
2N/A{
2N/A uint_t i;
2N/A uint_t lastpart;
2N/A size_t size;
2N/A
2N/A /* first we count how many partitions we have to send */
2N/A for (i = 0; i < MD_MAX_PARTS; i++) {
2N/A if ((mdvp->parts[i].start == 0) &&
2N/A (mdvp->parts[i].size == 0) &&
2N/A (mdvp->parts[i].tag != V_RESERVED)) {
2N/A continue;
2N/A }
2N/A /* if we are here, we know the partition is really used */
2N/A lastpart = i;
2N/A }
2N/A size = sizeof (struct dk_gpt) + (sizeof (struct dk_part) * lastpart);
2N/A *gpt = calloc(size, sizeof (char));
2N/A
2N/A (*gpt)->efi_nparts = lastpart + 1;
2N/A (*gpt)->efi_first_u_lba = mdvp->first_lba;
2N/A (*gpt)->efi_last_u_lba = mdvp->last_lba;
2N/A (*gpt)->efi_lbasize = mdvp->lbasize;
2N/A for (i = 0; (i < (*gpt)->efi_nparts); ++i) {
2N/A (*gpt)->efi_parts[i].p_start = mdvp->parts[i].start;
2N/A (*gpt)->efi_parts[i].p_size = mdvp->parts[i].size;
2N/A (*gpt)->efi_parts[i].p_tag = mdvp->parts[i].tag;
2N/A (*gpt)->efi_parts[i].p_flag = mdvp->parts[i].flag;
2N/A /*
2N/A * Due to the lack of a label for the entire partition table,
2N/A * we use p_name of the reserved partition
2N/A */
2N/A if (((*gpt)->efi_parts[i].p_tag == V_RESERVED) &&
2N/A (mdvp->typename != NULL)) {
2N/A (void) strlcpy((*gpt)->efi_parts[i].p_name,
2N/A mdvp->typename, EFI_PART_NAME_LEN);
2N/A }
2N/A }
2N/A}
2N/A
2N/A
2N/Avoid
2N/Actlr_cache_add(char *nm, int ty)
2N/A{
2N/A ctlr_cache_t **ccpp;
2N/A
2N/A for (ccpp = &ctlr_cache; *ccpp != NULL; ccpp = &(*ccpp)->ctlr_nx)
2N/A if (strcmp((*ccpp)->ctlr_nm, nm) == 0)
2N/A return;
2N/A
2N/A *ccpp = Zalloc(sizeof (ctlr_cache_t));
2N/A (*ccpp)->ctlr_nm = Strdup(nm);
2N/A (*ccpp)->ctlr_ty = ty;
2N/A}
2N/A
2N/Aint
2N/Actlr_cache_look(char *nm)
2N/A{
2N/A ctlr_cache_t *tcp;
2N/A
2N/A for (tcp = ctlr_cache; tcp != NULL; tcp = tcp->ctlr_nx)
2N/A if (strcmp(tcp->ctlr_nm, nm) == 0)
2N/A return (tcp->ctlr_ty);
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/A
2N/Avoid
2N/Ametaflushctlrcache(void)
2N/A{
2N/A ctlr_cache_t *cp, *np;
2N/A
2N/A for (cp = ctlr_cache, np = NULL; cp != NULL; cp = np) {
2N/A np = cp->ctlr_nx;
2N/A Free(cp->ctlr_nm);
2N/A Free(cp);
2N/A }
2N/A ctlr_cache = NULL;
2N/A}
2N/A
2N/A/*
2N/A * getdrvnode -- return the driver name based on mdname_t->bname
2N/A * Need to free pointer when finished.
2N/A */
2N/Achar *
2N/Agetdrvnode(mdname_t *np, md_error_t *ep)
2N/A{
2N/A char *devicespath;
2N/A char *drvnode;
2N/A char *cp;
2N/A
2N/A if ((devicespath = metagetdevicesname(np, ep)) == NULL)
2N/A return (NULL);
2N/A
2N/A /*
2N/A * At this point devicespath should be like the following
2N/A * "/devices/<unknow_and_dont_care>/xxxx@vvvv"
2N/A *
2N/A * There's a couple of 'if' statements below which could
2N/A * return an error condition, but I've decide to allow
2N/A * a more open approach regarding the mapping so as to
2N/A * not restrict possible future projects.
2N/A */
2N/A if (drvnode = strrchr(devicespath, '/'))
2N/A /*
2N/A * drvnode now just "xxxx@vvvv"
2N/A */
2N/A drvnode++;
2N/A
2N/A if (cp = strrchr(drvnode, '@'))
2N/A /*
2N/A * Now drvnode is just the driver name "xxxx"
2N/A */
2N/A *cp = '\0';
2N/A
2N/A cp = Strdup(drvnode);
2N/A Free(devicespath);
2N/A np->devicesname = NULL;
2N/A
2N/A return (cp);
2N/A}
2N/A
2N/A/*
2N/A * meta_load_dl -- open dynamic library using LDLIBRARYPATH, a debug
2N/A * environment variable METALDPATH, or the default location.
2N/A */
2N/Astatic void *
2N/Ameta_load_dl(mdname_t *np, md_error_t *ep)
2N/A{
2N/A char *drvnode;
2N/A char newpath[MAXPATHLEN];
2N/A char *p;
2N/A void *cookie;
2N/A
2N/A if ((drvnode = getdrvnode(np, ep)) != NULL) {
2N/A
2N/A /*
2N/A * Library seach algorithm:
2N/A * 1) Use LDLIBRARYPATH which is implied when a non-absolute
2N/A * path name is passed to dlopen()
2N/A * 2) Use the value of METALDPATH as the directory. Mainly
2N/A * used for debugging
2N/A * 3) Last search the default location of "/usr/lib"
2N/A */
2N/A (void) snprintf(newpath, sizeof (newpath), "lib%s.so.1",
2N/A drvnode);
2N/A if ((cookie = dlopen(newpath, RTLD_LAZY)) == NULL) {
2N/A if ((p = getenv("METALDPATH")) == NULL)
2N/A p = METALDPATH_DEFAULT;
2N/A (void) snprintf(newpath, sizeof (newpath),
2N/A "%s/lib%s.so.1", p, drvnode);
2N/A Free(drvnode);
2N/A if ((cookie = dlopen(newpath, RTLD_LAZY)) != NULL) {
2N/A /*
2N/A * Common failure here would be failing to
2N/A * find a libXX.so.1 such as libsd.so.1
2N/A * Some controllers will not have a library
2N/A * because there's no enclosure or name
2N/A * translation required.
2N/A */
2N/A return (cookie);
2N/A }
2N/A } else {
2N/A Free(drvnode);
2N/A return (cookie);
2N/A }
2N/A }
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * meta_match_names -- possibly convert the driver names returned by CINFO
2N/A */
2N/Astatic void
2N/Ameta_match_names(mdname_t *np, struct dk_cinfo *cp, mdcinfo_t *mdcp,
2N/A md_error_t *ep)
2N/A{
2N/A void *cookie;
2N/A meta_convert_e ((*fptr)(mdname_t *, struct dk_cinfo *, mdcinfo_t *,
2N/A md_error_t *));
2N/A
2N/A if ((cookie = meta_load_dl(np, ep)) != NULL) {
2N/A fptr = (meta_convert_e (*)(mdname_t *, struct dk_cinfo *,
2N/A mdcinfo_t *, md_error_t *))dlsym(cookie, "convert_path");
2N/A if (fptr != NULL)
2N/A (void) (*fptr)(np, cp, mdcp, ep);
2N/A (void) dlclose(cookie);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * meta_match_enclosure -- return any enclosure info if found
2N/A */
2N/Aint
2N/Ameta_match_enclosure(mdname_t *np, mdcinfo_t *mdcp, md_error_t *ep)
2N/A{
2N/A meta_enclosure_e e;
2N/A meta_enclosure_e ((*fptr)(mdname_t *, mdcinfo_t *,
2N/A md_error_t *));
2N/A void *cookie;
2N/A
2N/A if ((cookie = meta_load_dl(np, ep)) != NULL) {
2N/A fptr = (meta_enclosure_e (*)(mdname_t *, mdcinfo_t *,
2N/A md_error_t *))dlsym(cookie, "get_enclosure");
2N/A if (fptr != NULL) {
2N/A e = (*fptr)(np, mdcp, ep);
2N/A switch (e) {
2N/A case Enclosure_Error:
2N/A /*
2N/A * Looks like this library wanted to handle
2N/A * our device and had an internal error.
2N/A */
2N/A return (1);
2N/A
2N/A case Enclosure_Okay:
2N/A /*
2N/A * Found a library to handle the request so
2N/A * just return with data provided.
2N/A */
2N/A return (0);
2N/A
2N/A case Enclosure_Noop:
2N/A /*
2N/A * Need to continue the search
2N/A */
2N/A break;
2N/A }
2N/A }
2N/A (void) dlclose(cookie);
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Ameta_cinfo_to_md(mdname_t *np, struct dk_cinfo *cp, mdcinfo_t *mdcp,
2N/A md_error_t *ep)
2N/A{
2N/A /* default */
2N/A (void) memset(mdcp, '\0', sizeof (*mdcp));
2N/A (void) strncpy(mdcp->cname, cp->dki_cname,
2N/A min((sizeof (mdcp->cname) - 1), sizeof (cp->dki_cname)));
2N/A mdcp->ctype = MHD_CTLR_GENERIC;
2N/A mdcp->cnum = cp->dki_cnum;
2N/A (void) strncpy(mdcp->dname, cp->dki_dname,
2N/A min((sizeof (mdcp->dname) - 1), sizeof (cp->dki_dname)));
2N/A mdcp->unit = cp->dki_unit;
2N/A mdcp->maxtransfer = cp->dki_maxtransfer;
2N/A
2N/A /*
2N/A * See if the driver name returned from DKIOCINFO
2N/A * is valid or not. In somecases, such as the ap_dmd
2N/A * driver, we need to modify the name that's return
2N/A * for everything to work.
2N/A */
2N/A meta_match_names(np, cp, mdcp, ep);
2N/A
2N/A if (meta_match_enclosure(np, mdcp, ep))
2N/A return (-1);
2N/A
2N/A /* return success */
2N/A return (0);
2N/A}
2N/A
2N/Astatic void
2N/Ameta_vtoc_to_md(
2N/A struct extvtoc *vp,
2N/A mdvtoc_t *mdvp
2N/A)
2N/A{
2N/A char typename[sizeof (vp->v_asciilabel) + 1];
2N/A uint_t i;
2N/A
2N/A (void) memset(mdvp, '\0', sizeof (*mdvp));
2N/A (void) strncpy(typename, vp->v_asciilabel,
2N/A sizeof (vp->v_asciilabel));
2N/A typename[sizeof (typename) - 1] = '\0';
2N/A for (i = 0; ((i < sizeof (typename)) && (typename[i] != '\0')); ++i) {
2N/A if ((typename[i] == ' ') || (typename[i] == '\t')) {
2N/A typename[i] = '\0';
2N/A break;
2N/A }
2N/A }
2N/A mdvp->typename = Strdup(typename);
2N/A mdvp->nparts = vp->v_nparts;
2N/A for (i = 0; (i < vp->v_nparts); ++i) {
2N/A mdvp->parts[i].start = vp->v_part[i].p_start;
2N/A mdvp->parts[i].size = vp->v_part[i].p_size;
2N/A mdvp->parts[i].tag = vp->v_part[i].p_tag;
2N/A mdvp->parts[i].flag = vp->v_part[i].p_flag;
2N/A if (vp->v_part[i].p_start == 0 && vp->v_part[i].p_size > 0)
2N/A mdvp->parts[i].label = btodb(DK_LABEL_SIZE);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * free allocations in vtoc
2N/A */
2N/Avoid
2N/Ametafreevtoc(
2N/A mdvtoc_t *vtocp
2N/A)
2N/A{
2N/A if (vtocp->typename != NULL)
2N/A Free(vtocp->typename);
2N/A (void) memset(vtocp, 0, sizeof (*vtocp));
2N/A}
2N/A
2N/A/*
2N/A * return md types
2N/A */
2N/Amdvtoc_t *
2N/Ametagetvtoc(
2N/A mdname_t *np, /* only rname, drivenamep, are setup */
2N/A int nocache,
2N/A uint_t *partnop,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mddrivename_t *dnp = np->drivenamep;
2N/A struct dk_geom geom;
2N/A char *minor_name = NULL;
2N/A char *rname = np->rname;
2N/A int fd;
2N/A int partno;
2N/A int err = 0; /* saves errno from ioctl */
2N/A ddi_devid_t devid;
2N/A char *p;
2N/A
2N/A /* short circuit */
2N/A if ((! nocache) && (dnp->vtoc.nparts != 0)) {
2N/A if (partnop != NULL) {
2N/A /*
2N/A * the following assigment works because the
2N/A * mdname_t structs are always created as part
2N/A * of the drivenamep struct. When a user
2N/A * creates an mdname_t struct it either
2N/A * uses an existing drivenamep struct or creates
2N/A * a new one and then adds the mdname_t struct
2N/A * as part of its parts_val array. So what is
2N/A * being computed below is the slice offset in
2N/A * the parts_val array.
2N/A */
2N/A *partnop = np - np->drivenamep->parts.parts_val;
2N/A assert(*partnop < dnp->parts.parts_len);
2N/A }
2N/A return (&dnp->vtoc);
2N/A }
2N/A
2N/A /* can't get vtoc */
2N/A if (! nocache) {
2N/A switch (dnp->type) {
2N/A case MDT_ACCES:
2N/A case MDT_UNKNOWN:
2N/A (void) mdsyserror(ep, dnp->errnum, rname);
2N/A return (NULL);
2N/A }
2N/A }
2N/A
2N/A /* get all the info */
2N/A if ((fd = open(rname, (O_RDONLY|O_NDELAY), 0)) < 0) {
2N/A (void) mdsyserror(ep, errno, rname);
2N/A return (NULL);
2N/A }
2N/A
2N/A /*
2N/A * The disk is open so this is a good point to get the devid
2N/A * otherwise it will need to be done at another time which
2N/A * means reopening it.
2N/A */
2N/A if (devid_get(fd, &devid) != 0) {
2N/A /* there is no devid for the disk */
2N/A if (((p = getenv("MD_DEBUG")) != NULL) &&
2N/A (strstr(p, "DEVID") != NULL)) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s has no device id\n"), np->rname);
2N/A }
2N/A np->minor_name = (char *)NULL;
2N/A dnp->devid = NULL;
2N/A } else {
2N/A (void) devid_get_minor_name(fd, &minor_name);
2N/A /*
2N/A * The minor name could be NULL if the underlying
2N/A * device driver does not support 'minor names'.
2N/A * This means we do not use devid's for this device.
2N/A * SunCluster did driver does not support minor names.
2N/A */
2N/A if (minor_name != NULL) {
2N/A np->minor_name = Strdup(minor_name);
2N/A devid_str_free(minor_name);
2N/A dnp->devid = devid_str_encode(devid, NULL);
2N/A } else {
2N/A np->minor_name = (char *)NULL;
2N/A dnp->devid = NULL;
2N/A
2N/A if (((p = getenv("MD_DEBUG")) != NULL) &&
2N/A (strstr(p, "DEVID") != NULL)) {
2N/A (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
2N/A "%s no minor name (no devid)\n"),
2N/A np->rname);
2N/A }
2N/A }
2N/A devid_free(devid);
2N/A }
2N/A
2N/A /*
2N/A * if our drivenamep points to a device not supporting DKIOCGGEOM,
2N/A * it's likely to have an EFI label.
2N/A */
2N/A (void) memset(&geom, 0, sizeof (geom));
2N/A if (ioctl(fd, DKIOCGGEOM, &geom) != 0) {
2N/A err = errno;
2N/A if (err == ENOTTY) {
2N/A (void) mddeverror(ep, MDE_NOT_DISK, NODEV, rname);
2N/A (void) close(fd);
2N/A return (NULL);
2N/A } else if (err != ENOTSUP) {
2N/A (void) mdsyserror(ep, err, rname);
2N/A (void) close(fd);
2N/A return (NULL);
2N/A }
2N/A
2N/A }
2N/A /*
2N/A * If we are here, there was either no failure on DKIOCGGEOM or
2N/A * the failure was ENOTSUP
2N/A */
2N/A if (err == ENOTSUP) {
2N/A /* DKIOCGGEOM yielded ENOTSUP => try efi_alloc_and_read */
2N/A struct dk_gpt *gpt;
2N/A int save_errno;
2N/A md_unit_t *mdev = NULL;
2N/A mdsetname_t *sp = NULL;
2N/A
2N/A /* this also sets errno */
2N/A partno = efi_alloc_and_read(fd, &gpt);
2N/A save_errno = errno;
2N/A (void) close(fd);
2N/A if (partno < 0) {
2N/A efi_free(gpt);
2N/A (void) mdsyserror(ep, save_errno, rname);
2N/A return (NULL);
2N/A }
2N/A if (partno >= gpt->efi_nparts) {
2N/A efi_free(gpt);
2N/A (void) mddeverror(ep, MDE_INVALID_PART, NODEV64,
2N/A rname);
2N/A return (NULL);
2N/A }
2N/A
2N/A /* convert to our format */
2N/A metafreevtoc(&dnp->vtoc);
2N/A meta_efi_to_mdvtoc(gpt, &dnp->vtoc);
2N/A if (dnp->vtoc.nparts > MD_MAX_PARTS) {
2N/A efi_free(gpt);
2N/A (void) mddeverror(ep, MDE_TOO_MANY_PARTS, NODEV64,
2N/A rname);
2N/A return (NULL);
2N/A }
2N/A /*
2N/A * libmeta needs at least V_NUMPAR partitions.
2N/A * If we have an EFI partition with less than V_NUMPAR slices,
2N/A * we nevertheless reserve space for V_NUMPAR
2N/A */
2N/A
2N/A if (dnp->vtoc.nparts < V_NUMPAR) {
2N/A dnp->vtoc.nparts = V_NUMPAR;
2N/A }
2N/A
2N/A /*
2N/A * Is np a metadevice?
2N/A */
2N/A if (metaismeta(np)) {
2N/A sp = metasetnosetname(MD_MIN2SET(meta_getminor
2N/A (np->dev)), ep);
2N/A if (!sp || (mdev = meta_get_mdunit(sp, np, ep)) ==
2N/A NULL) {
2N/A efi_free(gpt);
2N/A (void) mddeverror(ep, MDE_NOT_META,
2N/A NODEV64, rname);
2N/A return (NULL);
2N/A }
2N/A }
2N/A
2N/A meta_efi_to_mdgeom(mdev, gpt, &dnp->geom);
2N/A Free(mdev);
2N/A efi_free(gpt);
2N/A } else {
2N/A /* no error on DKIOCGGEOM, try meta_getvtoc */
2N/A struct extvtoc vtoc;
2N/A
2N/A if (meta_getvtoc(fd, np->cname, &vtoc, &partno, ep) < 0) {
2N/A (void) close(fd);
2N/A return (NULL);
2N/A }
2N/A (void) close(fd);
2N/A
2N/A /* convert to our format */
2N/A meta_geom_to_md(&geom, &dnp->geom);
2N/A metafreevtoc(&dnp->vtoc);
2N/A meta_vtoc_to_md(&vtoc, &dnp->vtoc);
2N/A }
2N/A
2N/A /* fix up any drives which are now accessible */
2N/A if ((nocache) && (dnp->type == MDT_ACCES) &&
2N/A (dnp->vtoc.nparts == dnp->parts.parts_len)) {
2N/A dnp->type = MDT_COMP;
2N/A dnp->errnum = 0;
2N/A }
2N/A
2N/A /* save partno */
2N/A assert(partno < dnp->vtoc.nparts);
2N/A if (partnop != NULL)
2N/A *partnop = partno;
2N/A
2N/A /* return info */
2N/A return (&dnp->vtoc);
2N/A}
2N/A
2N/Astatic void
2N/Ameta_mdvtoc_to_vtoc(
2N/A mdvtoc_t *mdvp,
2N/A struct extvtoc *vp
2N/A)
2N/A{
2N/A uint_t i;
2N/A
2N/A (void) memset(&vp->v_part, '\0', sizeof (vp->v_part));
2N/A vp->v_nparts = (ushort_t)mdvp->nparts;
2N/A for (i = 0; (i < mdvp->nparts); ++i) {
2N/A vp->v_part[i].p_start = mdvp->parts[i].start;
2N/A vp->v_part[i].p_size = mdvp->parts[i].size;
2N/A vp->v_part[i].p_tag = mdvp->parts[i].tag;
2N/A vp->v_part[i].p_flag = mdvp->parts[i].flag;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Set the vtoc, but use the cached copy to get the info from.
2N/A * We write np->drivenamep->vtoc to disk.
2N/A * Before we can do this we read the vtoc in.
2N/A * if we're dealing with a metadevice and this metadevice is a 64 bit device
2N/A * we can use meta_getmdvtoc/meta_setmdvtoc
2N/A * else
2N/A * we use meta_getvtoc/meta_setvtoc but than we first have to convert
2N/A * dnp->vtoc (actually being a mdvtoc_t) into a vtoc_t
2N/A */
2N/Aint
2N/Ametasetvtoc(
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A char *rname = np->rname;
2N/A mddrivename_t *dnp = np->drivenamep;
2N/A int fd;
2N/A int err;
2N/A int save_errno;
2N/A struct dk_geom geom;
2N/A
2N/A if ((fd = open(rname, (O_RDONLY | O_NDELAY), 0)) < 0)
2N/A return (mdsyserror(ep, errno, rname));
2N/A
2N/A err = ioctl(fd, DKIOCGGEOM, &geom);
2N/A save_errno = errno;
2N/A if (err == 0) {
2N/A struct extvtoc vtoc;
2N/A
2N/A if (meta_getvtoc(fd, np->cname, &vtoc, NULL, ep) < 0) {
2N/A (void) close(fd);
2N/A return (-1);
2N/A }
2N/A
2N/A meta_mdvtoc_to_vtoc(&dnp->vtoc, &vtoc);
2N/A
2N/A if (meta_setvtoc(fd, np->cname, &vtoc, ep) < 0) {
2N/A (void) close(fd);
2N/A return (-1);
2N/A }
2N/A } else if (save_errno == ENOTSUP) {
2N/A struct dk_gpt *gpt;
2N/A int ret;
2N/A
2N/A /* allocation of gpt is done in meta_mdvtoc_to_efi */
2N/A meta_mdvtoc_to_efi(&dnp->vtoc, &gpt);
2N/A
2N/A ret = efi_write(fd, gpt);
2N/A save_errno = errno;
2N/A free(gpt);
2N/A if (ret != 0) {
2N/A (void) close(fd);
2N/A return (mdsyserror(ep, save_errno, rname));
2N/A } else {
2N/A (void) close(fd);
2N/A return (0);
2N/A }
2N/A
2N/A } else {
2N/A (void) close(fd);
2N/A return (mdsyserror(ep, save_errno, rname));
2N/A }
2N/A
2N/A (void) close(fd);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Amdgeom_t *
2N/Ametagetgeom(
2N/A mdname_t *np, /* only rname, drivenamep, are setup */
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A if (metagetvtoc(np, FALSE, NULL, ep) == NULL)
2N/A return (NULL);
2N/A return (&np->drivenamep->geom);
2N/A}
2N/A
2N/Amdcinfo_t *
2N/Ametagetcinfo(
2N/A mdname_t *np, /* only rname, drivenamep, are setup */
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A char *rname = np->rname;
2N/A mddrivename_t *dnp = np->drivenamep;
2N/A int fd;
2N/A struct dk_cinfo cinfo;
2N/A
2N/A /* short circuit */
2N/A if (dnp->cinfo.cname[0] != '\0')
2N/A return (&dnp->cinfo);
2N/A
2N/A /* get controller info */
2N/A if ((fd = open(rname, (O_RDONLY|O_NDELAY), 0)) < 0) {
2N/A (void) mdsyserror(ep, errno, rname);
2N/A return (NULL);
2N/A }
2N/A if (ioctl(fd, DKIOCINFO, &cinfo) != 0) {
2N/A int save = errno;
2N/A
2N/A (void) close(fd);
2N/A if (save == ENOTTY) {
2N/A (void) mddeverror(ep, MDE_NOT_DISK, NODEV64, rname);
2N/A } else {
2N/A (void) mdsyserror(ep, save, rname);
2N/A }
2N/A return (NULL);
2N/A }
2N/A (void) close(fd); /* sd/ssd bug */
2N/A
2N/A /* convert to our format */
2N/A if (meta_cinfo_to_md(np, &cinfo, &dnp->cinfo, ep) != 0)
2N/A return (NULL);
2N/A
2N/A /* return info */
2N/A return (&dnp->cinfo);
2N/A}
2N/A
2N/A/*
2N/A * get partition number
2N/A */
2N/Aint
2N/Ametagetpartno(
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdvtoc_t *vtocp;
2N/A uint_t partno;
2N/A
2N/A if ((vtocp = metagetvtoc(np, FALSE, &partno, ep)) == NULL)
2N/A return (-1);
2N/A assert(partno < vtocp->nparts);
2N/A return (partno);
2N/A}
2N/A
2N/A/*
2N/A * get size of device
2N/A */
2N/Adiskaddr_t
2N/Ametagetsize(
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdvtoc_t *vtocp;
2N/A uint_t partno;
2N/A
2N/A if ((vtocp = metagetvtoc(np, FALSE, &partno, ep)) == NULL)
2N/A return (MD_DISKADDR_ERROR);
2N/A assert(partno < vtocp->nparts);
2N/A return (vtocp->parts[partno].size);
2N/A}
2N/A
2N/A/*
2N/A * get label of device
2N/A */
2N/Adiskaddr_t
2N/Ametagetlabel(
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mdvtoc_t *vtocp;
2N/A uint_t partno;
2N/A
2N/A if ((vtocp = metagetvtoc(np, FALSE, &partno, ep)) == NULL)
2N/A return (MD_DISKADDR_ERROR);
2N/A assert(partno < vtocp->nparts);
2N/A return (vtocp->parts[partno].label);
2N/A}
2N/A
2N/A/*
2N/A * find out where database replicas end
2N/A */
2N/Astatic int
2N/Amddb_getendblk(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A diskaddr_t *endblkp,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_replicalist_t *rlp = NULL;
2N/A md_replicalist_t *rl;
2N/A
2N/A /* make sure we have a component */
2N/A *endblkp = 0;
2N/A if (metaismeta(np))
2N/A return (0);
2N/A
2N/A /* get replicas, quit if none */
2N/A if (metareplicalist(sp, MD_BASICNAME_OK | PRINT_FAST, &rlp, ep) < 0) {
2N/A if (! mdismddberror(ep, MDE_DB_NODB))
2N/A return (-1);
2N/A mdclrerror(ep);
2N/A return (0);
2N/A } else if (rlp == NULL)
2N/A return (0);
2N/A
2N/A /* go through all the replicas */
2N/A for (rl = rlp; (rl != NULL); rl = rl->rl_next) {
2N/A md_replica_t *rp = rl->rl_repp;
2N/A mdname_t *repnamep = rp->r_namep;
2N/A diskaddr_t dbend;
2N/A
2N/A if (np->dev != repnamep->dev)
2N/A continue;
2N/A dbend = rp->r_blkno + rp->r_nblk - 1;
2N/A if (dbend > *endblkp)
2N/A *endblkp = dbend;
2N/A }
2N/A
2N/A /* cleanup, return success */
2N/A metafreereplicalist(rlp);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * return cached start block
2N/A */
2N/Astatic diskaddr_t
2N/Ametagetend(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A diskaddr_t end_blk = MD_DISKADDR_ERROR;
2N/A
2N/A /* short circuit */
2N/A if (np->end_blk != MD_DISKADDR_ERROR)
2N/A return (np->end_blk);
2N/A
2N/A /* look for database locations */
2N/A if (mddb_getendblk(sp, np, &end_blk, ep) != 0)
2N/A return (MD_DISKADDR_ERROR);
2N/A
2N/A /* success */
2N/A np->end_blk = end_blk;
2N/A return (end_blk);
2N/A}
2N/A
2N/A/*
2N/A * does device have a metadb
2N/A */
2N/Aint
2N/Ametahasmddb(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A if (metagetend(sp, np, ep) == MD_DISKADDR_ERROR)
2N/A return (-1);
2N/A else if (np->end_blk > 0)
2N/A return (1);
2N/A else
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * return cached start block
2N/A */
2N/Adiskaddr_t
2N/Ametagetstart(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A diskaddr_t start_blk = MD_DISKADDR_ERROR;
2N/A
2N/A /* short circuit */
2N/A if (np->start_blk != MD_DISKADDR_ERROR)
2N/A return (np->start_blk);
2N/A
2N/A /* look for database locations */
2N/A if ((start_blk = metagetend(sp, np, ep)) == MD_DISKADDR_ERROR)
2N/A return (MD_DISKADDR_ERROR);
2N/A
2N/A /* check for label */
2N/A if (start_blk == 0) {
2N/A start_blk = metagetlabel(np, ep);
2N/A if (start_blk == MD_DISKADDR_ERROR) {
2N/A return (MD_DISKADDR_ERROR);
2N/A }
2N/A }
2N/A
2N/A /* roundup to next cylinder */
2N/A if (start_blk != 0) {
2N/A mdgeom_t *geomp;
2N/A
2N/A if ((geomp = metagetgeom(np, ep)) == NULL)
2N/A return (MD_DISKADDR_ERROR);
2N/A start_blk = roundup(start_blk, (geomp->nhead * geomp->nsect));
2N/A }
2N/A
2N/A /* success */
2N/A np->start_blk = start_blk;
2N/A return (start_blk);
2N/A}
2N/A
2N/A/*
2N/A * return cached devices name
2N/A */
2N/Achar *
2N/Ametagetdevicesname(
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A char path[MAXPATHLEN + 1];
2N/A int len;
2N/A
2N/A /* short circuit */
2N/A if (np->devicesname != NULL)
2N/A return (np->devicesname);
2N/A
2N/A /* follow symlink */
2N/A if ((len = readlink(np->bname, path, (sizeof (path) - 1))) < 0) {
2N/A (void) mdsyserror(ep, errno, np->bname);
2N/A return (NULL);
2N/A } else if (len >= sizeof (path)) {
2N/A (void) mdsyserror(ep, ENAMETOOLONG, np->bname);
2N/A return (NULL);
2N/A }
2N/A path[len] = '\0';
2N/A if ((len = strfind(path, "/devices/")) < 0) {
2N/A (void) mddeverror(ep, MDE_DEVICES_NAME, np->dev, np->bname);
2N/A return (NULL);
2N/A }
2N/A
2N/A /* return name */
2N/A np->devicesname = Strdup(path + len + strlen("/devices"));
2N/A return (np->devicesname);
2N/A}
2N/A
2N/A/*
2N/A * get metadevice misc name
2N/A */
2N/Achar *
2N/Ametagetmiscname(
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A mddrivename_t *dnp = np->drivenamep;
2N/A md_i_driverinfo_t mid;
2N/A
2N/A /* short circuit */
2N/A if (dnp->miscname != NULL)
2N/A return (dnp->miscname);
2N/A if (metachkmeta(np, ep) != 0)
2N/A return (NULL);
2N/A
2N/A /* get misc module from driver */
2N/A (void) memset(&mid, 0, sizeof (mid));
2N/A mid.mnum = meta_getminor(np->dev);
2N/A if (metaioctl(MD_IOCGET_DRVNM, &mid, &mid.mde, np->cname) != 0) {
2N/A (void) mdstealerror(ep, &mid.mde);
2N/A return (NULL);
2N/A }
2N/A
2N/A /* return miscname */
2N/A dnp->miscname = Strdup(MD_PNTDRIVERNAME(&mid));
2N/A return (dnp->miscname);
2N/A}
2N/A
2N/A/*
2N/A * get unit structure from driver
2N/A */
2N/Amd_unit_t *
2N/Ameta_get_mdunit(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A md_i_get_t mig;
2N/A char *miscname = NULL;
2N/A
2N/A /* should have a set */
2N/A assert(sp != NULL);
2N/A assert(sp->setno == MD_MIN2SET(meta_getminor(np->dev)));
2N/A
2N/A /* get size of unit structure */
2N/A if (metachkmeta(np, ep) != 0)
2N/A return (NULL);
2N/A if ((miscname = metagetmiscname(np, ep)) == NULL)
2N/A return (NULL);
2N/A (void) memset(&mig, '\0', sizeof (mig));
2N/A MD_SETDRIVERNAME(&mig, miscname, sp->setno);
2N/A mig.id = meta_getminor(np->dev);
2N/A if (metaioctl(MD_IOCGET, &mig, &mig.mde, np->cname) != 0) {
2N/A (void) mdstealerror(ep, &mig.mde);
2N/A return (NULL);
2N/A }
2N/A
2N/A /* get actual unit structure */
2N/A assert(mig.size > 0);
2N/A mig.mdp = (uintptr_t)Zalloc(mig.size);
2N/A if (metaioctl(MD_IOCGET, &mig, &mig.mde, np->cname) != 0) {
2N/A (void) mdstealerror(ep, &mig.mde);
2N/A Free((void *)(uintptr_t)mig.mdp);
2N/A return (NULL);
2N/A }
2N/A
2N/A return ((md_unit_t *)(uintptr_t)mig.mdp);
2N/A}
2N/A
2N/A/*
2N/A * free metadevice unit
2N/A */
2N/Avoid
2N/Ameta_free_unit(
2N/A mddrivename_t *dnp
2N/A)
2N/A{
2N/A if (dnp->unitp != NULL) {
2N/A switch (dnp->unitp->type) {
2N/A case MD_DEVICE:
2N/A meta_free_stripe((md_stripe_t *)dnp->unitp);
2N/A break;
2N/A case MD_METAMIRROR:
2N/A meta_free_mirror((md_mirror_t *)dnp->unitp);
2N/A break;
2N/A case MD_METATRANS:
2N/A meta_free_trans((md_trans_t *)dnp->unitp);
2N/A break;
2N/A case MD_METARAID:
2N/A meta_free_raid((md_raid_t *)dnp->unitp);
2N/A break;
2N/A case MD_METASP:
2N/A meta_free_sp((md_sp_t *)dnp->unitp);
2N/A break;
2N/A default:
2N/A assert(0);
2N/A break;
2N/A }
2N/A dnp->unitp = NULL;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * free metadevice name info
2N/A */
2N/Avoid
2N/Ameta_invalidate_name(
2N/A mdname_t *namep
2N/A)
2N/A{
2N/A mddrivename_t *dnp = namep->drivenamep;
2N/A
2N/A /* get rid of cached name info */
2N/A if (namep->devicesname != NULL) {
2N/A Free(namep->devicesname);
2N/A namep->devicesname = NULL;
2N/A }
2N/A namep->key = MD_KEYBAD;
2N/A namep->start_blk = -1;
2N/A namep->end_blk = -1;
2N/A
2N/A /* get rid of cached drivename info */
2N/A (void) memset(&dnp->geom, 0, sizeof (dnp->geom));
2N/A (void) memset(&dnp->cinfo, 0, sizeof (dnp->cinfo));
2N/A metafreevtoc(&dnp->vtoc);
2N/A metaflushsidenames(dnp);
2N/A dnp->side_names_key = MD_KEYBAD;
2N/A if (dnp->miscname != NULL) {
2N/A Free(dnp->miscname);
2N/A dnp->miscname = NULL;
2N/A }
2N/A meta_free_unit(dnp);
2N/A}
2N/A
2N/A/*
2N/A * get metadevice unit
2N/A */
2N/Amd_common_t *
2N/Ameta_get_unit(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A char *miscname;
2N/A
2N/A /* short circuit */
2N/A if (np->drivenamep->unitp != NULL)
2N/A return (np->drivenamep->unitp);
2N/A if (metachkmeta(np, ep) != 0)
2N/A return (NULL);
2N/A
2N/A /* dispatch */
2N/A if ((miscname = metagetmiscname(np, ep)) == NULL)
2N/A return (NULL);
2N/A else if (strcmp(miscname, MD_STRIPE) == 0)
2N/A return ((md_common_t *)meta_get_stripe(sp, np, ep));
2N/A else if (strcmp(miscname, MD_MIRROR) == 0)
2N/A return ((md_common_t *)meta_get_mirror(sp, np, ep));
2N/A else if (strcmp(miscname, MD_TRANS) == 0)
2N/A return ((md_common_t *)meta_get_trans(sp, np, ep));
2N/A else if (strcmp(miscname, MD_RAID) == 0)
2N/A return ((md_common_t *)meta_get_raid(sp, np, ep));
2N/A else if (strcmp(miscname, MD_SP) == 0)
2N/A return ((md_common_t *)meta_get_sp(sp, np, ep));
2N/A else {
2N/A (void) mdmderror(ep, MDE_UNKNOWN_TYPE, meta_getminor(np->dev),
2N/A np->cname);
2N/A return (NULL);
2N/A }
2N/A}
2N/A
2N/A
2N/Aint
2N/Ameta_isopen(
2N/A mdsetname_t *sp,
2N/A mdname_t *np,
2N/A md_error_t *ep,
2N/A mdcmdopts_t options
2N/A)
2N/A{
2N/A md_isopen_t d;
2N/A
2N/A if (metachkmeta(np, ep) != 0)
2N/A return (-1);
2N/A
2N/A (void) memset(&d, '\0', sizeof (d));
2N/A d.dev = np->dev;
2N/A if (metaioctl(MD_IOCISOPEN, &d, &d.mde, np->cname) != 0)
2N/A return (mdstealerror(ep, &d.mde));
2N/A
2N/A /*
2N/A * shortcut: if the device is open, no need to check on other nodes,
2N/A * even in case of a mn metadevice
2N/A * Also return in case we're told not to check on other nodes.
2N/A */
2N/A if ((d.isopen != 0) || ((options & MDCMD_MN_OPEN_CHECK) == 0)) {
2N/A return (d.isopen);
2N/A }
2N/A
2N/A /*
2N/A * If the device is closed locally, but it's a mn device,
2N/A * check on all other nodes, too
2N/A */
2N/A if (sp->setno != MD_LOCAL_SET) {
2N/A (void) metaget_setdesc(sp, ep); /* not supposed to fail */
2N/A if (sp->setdesc->sd_flags & MD_SR_MN) {
2N/A int err = 0;
2N/A md_mn_result_t *resp;
2N/A /*
2N/A * This message is never directly issued.
2N/A * So we launch it with a suspend override flag.
2N/A * If the commd is suspended, and this message comes
2N/A * along it must be sent due to replaying a metainit or
2N/A * similar. In that case we don't want this message to
2N/A * be blocked.
2N/A * If the commd is not suspended, the flag does no harm.
2N/A * Additionally we don't want the result of the message
2N/A * cached in the MCT, because we want uptodate results,
2N/A * and the message doesn't need being logged either.
2N/A * Hence NO_LOG and NO_MCT
2N/A */
2N/A err = mdmn_send_message(sp->setno,
2N/A MD_MN_MSG_CLU_CHECK, MD_MSGF_NO_MCT |
2N/A MD_MSGF_STOP_ON_ERROR | MD_MSGF_NO_LOG |
2N/A MD_MSGF_OVERRIDE_SUSPEND, 0, (char *)&d,
2N/A sizeof (md_isopen_t), &resp, ep);
2N/A if (err == 0) {
2N/A d.isopen = resp->mmr_exitval;
2N/A } else {
2N/A /*
2N/A * in case some error occurred,
2N/A * we better say the device is open
2N/A */
2N/A d.isopen = 1;
2N/A }
2N/A if (resp != (md_mn_result_t *)NULL) {
2N/A free_result(resp);
2N/A }
2N/A
2N/A }
2N/A }
2N/A
2N/A return (d.isopen);
2N/A}
2N/A
2N/A/*
2N/A * Return the minor name associated with a given disk slice
2N/A */
2N/Achar *
2N/Ameta_getminor_name(
2N/A char *devname,
2N/A md_error_t *ep
2N/A)
2N/A{
2N/A int fd = -1;
2N/A char *minor_name = NULL;
2N/A char *ret_minor_name = NULL;
2N/A
2N/A if (devname == NULL)
2N/A return (NULL);
2N/A
2N/A if ((fd = open(devname, O_RDONLY|O_NDELAY, 0)) < 0) {
2N/A (void) mdsyserror(ep, errno, devname);
2N/A return (NULL);
2N/A }
2N/A
2N/A if (devid_get_minor_name(fd, &minor_name) == 0) {
2N/A ret_minor_name = Strdup(minor_name);
2N/A devid_str_free(minor_name);
2N/A }
2N/A
2N/A (void) close(fd);
2N/A return (ret_minor_name);
2N/A}