/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include /* * Just in case we're not in a build environment, make sure that * TEXT_DOMAIN gets set to something. */ #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif /* * Macros to produce a quoted string containing the value of a * preprocessor macro. For example, if SIZE is defined to be 256, * VAL2STR(SIZE) is "256". This is used to construct format * strings for scanf-family functions below. */ #define QUOTE(x) #x #define VAL2STR(x) QUOTE(x) extern char *getfullblkname(); extern char *getfullrawname(); /* * caches */ static mdsetnamelist_t *setlistp = NULL; static mddrivenamelist_t *drivelistp = NULL; static mdnamelist_t *fastnmlp = NULL; static mdhspnamelist_t *hsplistp = NULL; /* * leak proof name conversion */ static char * rawname( char *uname ) { char *p; struct stat sbuf1, sbuf2; if ((p = getfullrawname(uname)) == NULL) { return (NULL); } else if (*p == '\0') { Free(p); return (NULL); } else { if (stat(uname, &sbuf1) != 0) { (void) printf(dgettext(TEXT_DOMAIN, "device to mount in /etc/vfstab is " "invalid for device %s\n"), uname); exit(1); } if (stat(p, &sbuf2) != 0) { (void) printf(dgettext(TEXT_DOMAIN, "device to fsck in /etc/vfstab is " "invalid for raw device %s\n"), p); exit(1); } if (sbuf1.st_rdev != sbuf2.st_rdev) { (void) printf(dgettext(TEXT_DOMAIN, "/etc/vfstab entries inconsistent on " "line containing device %s\n"), uname); exit(1); } if ((sbuf1.st_mode & S_IFBLK) == 0) { (void) printf(dgettext(TEXT_DOMAIN, "/etc/vfstab device to mount is not a " "block device for device %s\n"), uname); exit(1); } if ((sbuf2.st_mode & S_IFCHR) == 0) { (void) printf(dgettext(TEXT_DOMAIN, "/etc/vfstab device to fsck is not a " "raw device for device %s\n"), p); exit(1); } return (p); } } char * blkname( char *uname ) { char *p; if ((p = getfullblkname(uname)) == NULL) { return (NULL); } else if (*p == '\0') { Free(p); return (NULL); } else { return (p); } } /* * parse up metadevice name */ static int parse_metadevice( char *uname, char **snamep, unit_t *unitp ) { char *sname = Malloc(strlen(uname) + 1); char *tname = Malloc(strlen(uname) + 1); unit_t unit; int len; char *up; char *tp; int lcws; /* last character was slash */ /* handle dont cares */ if (unitp == NULL) unitp = &unit; /* Now copy uname to tname by throwing away any duplicate '/' */ for (lcws = 0, tp = tname, up = uname; *up; up++) { if (lcws) { if (*up == '/') { continue; } else { lcws = 0; } } if (*up == '/') { lcws = 1; } *tp++ = *up; /* ++ is done by for loop */ } *tp = '\0'; /* without set */ if ((sscanf(tname, "d%lu%n", unitp, &len) == 1) && (strlen(tname) == len) && ((long)*unitp >= 0)) { if (snamep != NULL) *snamep = NULL; Free(sname); Free(tname); return (0); } /* fully-qualified without set */ if (((sscanf(tname, "/dev/md/dsk/d%lu%n", unitp, &len) == 1) && (strlen(tname) == len) && ((long)*unitp >= 0)) || ((sscanf(tname, "/dev/md/rdsk/d%lu%n", unitp, &len) == 1) && (strlen(tname) == len) && ((long)*unitp >= 0))) { if (snamep != NULL) *snamep = Strdup(MD_LOCAL_NAME); Free(sname); Free(tname); return (0); } /* with set */ if (((sscanf(tname, "%[^/]/d%lu%n", sname, unitp, &len) == 2) && (strlen(tname) == len) && ((long)*unitp >= 0)) || ((sscanf(tname, "/dev/md/%[^/]/dsk/d%lu%n", sname, unitp, &len) == 2) && (strlen(tname) == len) && ((long)*unitp >= 0)) || ((sscanf(tname, "/dev/md/%[^/]/rdsk/d%lu%n", sname, unitp, &len) == 2) && (strlen(tname) == len) && ((long)*unitp >= 0))) { if (snamep != NULL) { *snamep = sname; } else { Free(sname); } Free(tname); return (0); } /* no match */ if (snamep != NULL) *snamep = NULL; Free(sname); Free(tname); return (-1); } /* * FUNCTION: parse_device() * INPUT: sp - pointer to setname struct * uname - Name of either a hotspare pool or metadevice * This can either be a fully qualified path or * in the form [set name/]device * OUTPUT: setnamep - name of the set that uname is in * uname - name of the hotspare pools or metadevice * only contains the name of the device with all * other path information stripped off. * PURPOSE: Parse uname and sp into the set name and device name strings. * If the set name is specified as part of uname then use that * otherwise attempt to get the set name from sp. */ static void parse_device( mdsetname_t *sp, char *uname, char **setnamep /* dynamically alloced - caller must free */ ) { char setname[FILENAME_MAX+1]; char *tname = Malloc(strlen(uname) + 1); int len; char *up; char *tp; int lcws; /* last character was slash */ /* Now copy uname to tname by throwing away any duplicate '/' */ for (lcws = 0, tp = tname, up = uname; *up; up++) { if (lcws) { if (*up == '/') { continue; } else { lcws = 0; } } if (*up == '/') { lcws = 1; } *tp++ = *up; /* ++ is done by for loop */ } *tp = '\0'; /* fully-qualified - local set */ if (((sscanf(tname, "/dev/md/dsk/%" VAL2STR(FILENAME_MAX) "s%n", uname, &len) == 1) && (strlen(tname) == len)) || ((sscanf(tname, "/dev/md/rdsk/%" VAL2STR(FILENAME_MAX) "s%n", uname, &len) == 1) && (strlen(tname) == len))) { if (setnamep != NULL) *setnamep = NULL; Free(tname); return; } /* with setname specified - either fully qualified and relative spec */ if (((sscanf(tname, "%" VAL2STR(FILENAME_MAX) "s/%" VAL2STR(FILENAME_MAX) "s%n", setname, uname, &len) == 2) && (strlen(tname) == len)) || ((sscanf(tname, "/dev/md/%[^/]/dsk/%" VAL2STR(FILENAME_MAX) "s%n", setname, uname, &len) == 2) && (strlen(tname) == len)) || ((sscanf(tname, "/dev/md/%[^/]/rdsk/%" VAL2STR(FILENAME_MAX) "s%n", setname, uname, &len) == 2) && (strlen(tname) == len))) { if (setnamep != NULL) { *setnamep = Strdup(setname); } Free(tname); return; } /* without setname specified */ (void) strcpy(uname, tname); if (setnamep != NULL) { if (sp != NULL && !metaislocalset(sp)) *setnamep = Strdup(sp->setname); else *setnamep = NULL; } Free(tname); } /* * parse up hotspare pool name */ static int parse_hsp( char *uname, char **snamep, hsp_t *hspp ) { char *sname = Malloc(strlen(uname) + 1); hsp_t hsp; int len; /* handle dont cares */ if (hspp == NULL) hspp = &hsp; /* without set */ if ((sscanf(uname, "hsp%03u%n", hspp, &len) == 1) && (strlen(uname) == len) && ((long)*hspp >= 0)) { if (snamep != NULL) *snamep = NULL; Free(sname); return (0); } /* with set */ if ((sscanf(uname, "%[^/]/hsp%03u%n", sname, hspp, &len) == 2) && (strlen(uname) == len) && ((long)*hspp >= 0)) { if (snamep != NULL) { *snamep = sname; } else { Free(sname); } return (0); } /* no match */ Free(sname); return (-1); } /* * canonicalize metadevice name */ static char * canon_metadevice( char *sname, unit_t unit ) { char *cname; size_t len; if ((sname == NULL) || (strcmp(sname, MD_LOCAL_NAME) == 0)) { len = strlen("d") + 20 + 1; cname = Malloc(len); (void) snprintf(cname, len, "d%lu", unit); } else { len = strlen(sname) + strlen("/d") + 20 + 1; cname = Malloc(len); (void) snprintf(cname, len, "%s/d%lu", sname, unit); } return (cname); } /* * canonicalize hotspare pool name */ static char * canon_hsp( char *sname, hsp_t hsp ) { char *cname; size_t len; if ((sname == NULL) || (strcmp(sname, MD_LOCAL_NAME) == 0)) { cname = Malloc(strlen("hsp000") + 1); (void) sprintf(cname, "hsp%03u", hsp); } else { len = strlen(sname) + strlen("/hsp000") + 1; cname = Malloc(len); (void) snprintf(cname, len, "%s/hsp%03lu", sname, hsp); } return (cname); } /* * canonicalize name, return type * * NOTE: this is really only for use by meta_tab* */ char * meta_canonicalize( mdsetname_t *sp, char *uname ) { char *sname = NULL; char *cname; /* return the set name and dev name */ parse_device(sp, uname, &sname); if (sname == NULL) cname = Strdup(uname); else { size_t cname_len; cname_len = strlen(uname) + strlen(sname) + 2; cname = Malloc(cname_len); (void) snprintf( cname, cname_len, "%s/%s", sname, uname); Free(sname); } return (cname); } /* * check that name is a metadevice */ int is_metaname( char *uname ) { if (parse_metadevice(uname, NULL, NULL) == 0) return (1); else return (0); } /* * check that name is a hotspare pool */ int is_hspname( char *uname ) { if (parse_hsp(uname, NULL, NULL) == 0) return (1); else return (0); } /* * mdsetname_t stuff */ /* * initialize setname */ static void metainitsetname( mdsetname_t *sp ) { (void) memset(sp, '\0', sizeof (*sp)); } static void metafreesetdesc(md_set_desc *sd) { md_mnnode_desc *nd; if (MD_MNSET_DESC(sd)) { nd = sd->sd_nodelist; while (nd) { sd->sd_nodelist = nd->nd_next; Free(nd); nd = sd->sd_nodelist; } } metafreedrivedesc(&sd->sd_drvs); Free(sd); } /* * free allocated setname */ static void metafreesetname( mdsetname_t *sp ) { if (sp->setname != NULL) Free(sp->setname); if (sp->setdesc != NULL) metafreesetdesc(sp->setdesc); metainitsetname(sp); } /* * flush the setname cache */ static void metaflushsetnames() { mdsetnamelist_t *p, *n; for (p = setlistp, n = NULL; (p != NULL); p = n) { n = p->next; metafreesetname(p->sp); Free(p->sp); Free(p); } setlistp = NULL; } /* * get set number */ static int getsetno( char *sname, set_t *setnop, md_error_t *ep ) { md_set_record *sr; size_t len; /* local set */ if ((sname == NULL) || (strcmp(sname, MD_LOCAL_NAME) == 0)) { *setnop = 0; return (0); } /* shared set */ if ((sr = getsetbyname(sname, ep)) == NULL) { if (mdisrpcerror(ep, RPC_PROGNOTREGISTERED)) { char *p; len = strlen(sname) + 30; p = Malloc(len); (void) snprintf(p, len, "setname \"%s\"", sname); (void) mderror(ep, MDE_NO_SET, p); Free(p); } return (-1); } *setnop = sr->sr_setno; free_sr(sr); return (0); } /* * find setname from name */ mdsetname_t * metasetname( char *sname, md_error_t *ep ) { mdsetnamelist_t **tail; set_t setno; mdsetname_t *sp; /* look for cached value first */ assert(sname != NULL); for (tail = &setlistp; (*tail != NULL); tail = &(*tail)->next) { sp = (*tail)->sp; if (strcmp(sp->setname, sname) == 0) { return (sp); } } /* setup set */ if (getsetno(sname, &setno, ep) != 0) return (NULL); /* allocate new list element and setname */ *tail = Zalloc(sizeof (**tail)); sp = (*tail)->sp = Zalloc(sizeof (*sp)); sp->setname = Strdup(sname); sp->setno = setno; sp->lockfd = MD_NO_LOCK; return (sp); } /* * find setname from setno */ mdsetname_t * metasetnosetname( set_t setno, md_error_t *ep ) { mdsetnamelist_t *slp; mdsetname_t *sp; md_set_record *sr; /* look for cached value first */ for (slp = setlistp; (slp != NULL); slp = slp->next) { sp = slp->sp; if (sp->setno == setno) return (sp); } /* local set */ if (setno == MD_LOCAL_SET) return (metasetname(MD_LOCAL_NAME, ep)); /* shared set */ if ((sr = getsetbynum(setno, ep)) == NULL) return (NULL); sp = metasetname(sr->sr_setname, ep); free_sr(sr); return (sp); } mdsetname_t * metafakesetname( set_t setno, char *sname ) { mdsetnamelist_t **tail; mdsetname_t *sp; /* look for cached value first */ for (tail = &setlistp; (*tail != NULL); tail = &(*tail)->next) { sp = (*tail)->sp; if (sp->setno == setno) { if ((sp->setname == NULL) && (sname != NULL)) sp->setname = Strdup(sname); return (sp); } } /* allocate new list element and setname */ *tail = Zalloc(sizeof (**tail)); sp = (*tail)->sp = Zalloc(sizeof (*sp)); if (sname != NULL) sp->setname = Strdup(sname); sp->setno = setno; sp->lockfd = MD_NO_LOCK; return (sp); } /* * setup set record (sr) and cache it in the mdsetname_t struct */ md_set_desc * sr2setdesc( md_set_record *sr ) { md_set_desc *sd; int i; md_mnset_record *mnsr; md_mnnode_desc *nd, *nd_prev = 0; md_mnnode_record *nr; md_error_t status = mdnullerror; md_error_t *ep = &status; int nodecnt, nrcnt; mndiskset_membershiplist_t *nl, *nl2; sd = Zalloc(sizeof (*sd)); sd->sd_ctime = sr->sr_ctime; sd->sd_genid = sr->sr_genid; sd->sd_setno = sr->sr_setno; sd->sd_flags = sr->sr_flags; if (MD_MNSET_DESC(sd)) { mnsr = (md_mnset_record *)sr; (void) strlcpy(sd->sd_mn_master_nodenm, mnsr->sr_master_nodenm, sizeof (sd->sd_mn_master_nodenm)); sd->sd_mn_master_nodeid = mnsr->sr_master_nodeid; if (strcmp(mnsr->sr_master_nodenm, mynode()) == 0) { sd->sd_mn_am_i_master = 1; } /* * Get membershiplist from API routine. If there's * an error, just use a NULL nodelist. */ if (meta_read_nodelist(&nodecnt, &nl, ep) == -1) { nodecnt = 0; /* no nodes are alive */ nl = NULL; } nr = mnsr->sr_nodechain; nrcnt = 0; /* * Node descriptor node list must be built in * ascending order of nodeid. The nodechain * in the mnset record is in ascending order, * so just make them the same. */ while (nr) { nd = Zalloc(sizeof (*nd)); if (nd_prev) { nd_prev->nd_next = nd; } else { sd->sd_nodelist = nd; } nd->nd_ctime = nr->nr_ctime; nd->nd_genid = nr->nr_genid; nd->nd_flags = nr->nr_flags; (void) strlcpy(nd->nd_nodename, nr->nr_nodename, sizeof (nd->nd_nodename)); nd->nd_nodeid = nr->nr_nodeid; if (strcmp(nd->nd_nodename, mynode()) == 0) { sd->sd_mn_mynode = nd; } if (nd->nd_nodeid == sd->sd_mn_master_nodeid) { sd->sd_mn_masternode = nd; } /* * If node is marked ALIVE, then set priv_ic * from membership list. During the early part * of a reconfig cycle, the membership list may * have been changed, (a node entering or leaving * the cluster), but rpc.metad hasn't flushed * its data yet. So, if node is marked alive, but * is no longer in the membership list (node has * left the cluster) then just leave priv_ic to NULL. */ if (nd->nd_flags & MD_MN_NODE_ALIVE) { nl2 = nl; while (nl2) { if (nl2->msl_node_id == nd->nd_nodeid) { (void) strlcpy(nd->nd_priv_ic, nl2->msl_node_addr, sizeof (nd->nd_priv_ic)); break; } nl2 = nl2->next; } } nr = nr->nr_next; nrcnt++; nd_prev = nd; } sd->sd_mn_numnodes = nrcnt; if (nodecnt) meta_free_nodelist(nl); /* Just copying to keep consistent view between sr & sd */ (void) strlcpy(sd->sd_nodes[0], mnsr->sr_nodes_bw_compat[0], sizeof (sd->sd_nodes[0])); } else { for (i = 0; i < MD_MAXSIDES; i++) (void) strlcpy(sd->sd_nodes[i], sr->sr_nodes[i], sizeof (sd->sd_nodes[i])); } sd->sd_med = sr->sr_med; /* structure assignment */ return (sd); } md_set_desc * metaget_setdesc( mdsetname_t *sp, md_error_t *ep ) { md_set_record *sr; if (sp->setdesc != NULL) return (sp->setdesc); if (sp->setname != NULL) { if ((sr = getsetbyname(sp->setname, ep)) != NULL) { sp->setdesc = sr2setdesc(sr); free_sr(sr); return (sp->setdesc); } } if (sp->setno > 0) { if ((sr = getsetbynum(sp->setno, ep)) != NULL) { sp->setdesc = sr2setdesc(sr); free_sr(sr); return (sp->setdesc); } } return (NULL); } void metaflushsetname(mdsetname_t *sp) { if (sp == NULL) return; if (sp->setdesc == NULL) return; metafreesetdesc(sp->setdesc); sp->setdesc = NULL; } /* * check for local set */ int metaislocalset( mdsetname_t *sp ) { assert(sp->setname != NULL); if (strcmp(sp->setname, MD_LOCAL_NAME) == 0) { assert(sp->setno == MD_LOCAL_SET); return (1); } else { assert(sp->setno != MD_LOCAL_SET); return (0); } } /* * check for same set */ int metaissameset( mdsetname_t *sp1, mdsetname_t *sp2 ) { if (strcmp(sp1->setname, sp2->setname) == 0) { assert(sp1->setno == sp2->setno); return (1); } else { assert(sp1->setno != sp2->setno); return (0); } } /* * check to see if set changed */ static int chkset( mdsetname_t **spp, char *sname, md_error_t *ep ) { /* if we already have a set, make sure it's the same */ if (*spp != NULL) { if ((*spp)->setname != sname && strcmp((*spp)->setname, sname) != 0) { return (mderror(ep, MDE_SET_DIFF, sname)); } return (0); } /* otherwise store new set name and number */ if ((*spp = metasetname(sname, ep)) == NULL) { return (-1); } /* return success */ return (0); } /* * check to see if set changed from default */ static int chksetname( mdsetname_t **spp, char *sname, md_error_t *ep ) { /* default to *spp's setname, or if that is NULL to MD_LOCAL_NAME */ if (sname == NULL) { if (*spp) { sname = (*spp)->setname; } else { sname = MD_LOCAL_NAME; } } /* see if changed */ return (chkset(spp, sname, ep)); } /* * check setname from setno */ static int chksetno( mdsetname_t **spp, set_t setno, md_error_t *ep ) { md_set_record *sr; int rval; /* local set */ if (setno == 0) return (chkset(spp, MD_LOCAL_NAME, ep)); /* shared set */ if ((sr = getsetbynum(setno, ep)) == NULL) return (-1); rval = chkset(spp, sr->sr_setname, ep); free_sr(sr); return (rval); } /* * mddrivename_t stuff */ /* * initialize name */ static void metainitname( mdname_t *np ) { (void) memset(np, 0, sizeof (*np)); np->dev = NODEV64; np->key = MD_KEYBAD; np->end_blk = -1; np->start_blk = -1; } /* * free allocated name */ static void metafreename( mdname_t *np ) { if (np->cname != NULL) Free(np->cname); if (np->bname != NULL) Free(np->bname); if (np->rname != NULL) Free(np->rname); if (np->devicesname != NULL) Free(np->devicesname); metainitname(np); } /* * initialize drive name */ static void metainitdrivename( mddrivename_t *dnp ) { (void) memset(dnp, 0, sizeof (*dnp)); dnp->side_names_key = MD_KEYBAD; } /* * flush side names */ void metaflushsidenames( mddrivename_t *dnp ) { mdsidenames_t *p, *n; for (p = dnp->side_names, n = NULL; (p != NULL); p = n) { n = p->next; if (p->dname != NULL) Free(p->dname); if (p->cname != NULL) Free(p->cname); Free(p); } dnp->side_names = NULL; } /* * free drive name */ void metafreedrivename( mddrivename_t *dnp ) { uint_t slice; if (dnp->cname != NULL) Free(dnp->cname); if (dnp->rname != NULL) Free(dnp->rname); metafreevtoc(&dnp->vtoc); for (slice = 0; (slice < dnp->parts.parts_len); ++slice) metafreename(&dnp->parts.parts_val[slice]); if (dnp->parts.parts_val != NULL) Free(dnp->parts.parts_val); metaflushsidenames(dnp); if (dnp->miscname != NULL) Free(dnp->miscname); meta_free_unit(dnp); metainitdrivename(dnp); } /* * flush the drive name cache */ static void metaflushdrivenames() { mddrivenamelist_t *p, *n; for (p = drivelistp, n = NULL; (p != NULL); p = n) { n = p->next; metafreedrivename(p->drivenamep); Free(p->drivenamep); Free(p); } drivelistp = NULL; } /* * peel off s%u from name */ char * metadiskname( char *name ) { char *p, *e; char onmb[BUFSIZ+1], cnmb[BUFSIZ]; uint_t d = 0; int l = 0; int cl = strlen(name); if (is_metaname(name)) return (Strdup(name)); /* * Handle old style names, which are of the form /dev/rXXNN[a-h]. */ if (sscanf(name, "/dev/r%" VAL2STR(BUFSIZ) "[^0-9/]%u%*[a-h]%n", onmb, &d, &l) == 2 && l == cl) { (void) snprintf(cnmb, sizeof (cnmb), "/dev/r%s%u", onmb, d); return (Strdup(cnmb)); } /* * Handle old style names, which are of the form /dev/XXNN[a-h]. */ if (sscanf(name, "/dev/%" VAL2STR(BUFSIZ) "[^0-9/]%u%*[a-h]%n", onmb, &d, &l) == 2 && l == cl) { (void) snprintf(cnmb, sizeof (cnmb), "/dev/%s%u", onmb, d); return (Strdup(cnmb)); } /* gobble number and 's' */ p = e = name + strlen(name) - 1; for (; (p > name); --p) { if (!isdigit(*p)) break; } if ((p == e) || (p <= name)) return (Strdup(name)); if (*p != 's' && strchr("dt", *p) == NULL) return (Strdup(name)); else if (strchr("dt", *p) != NULL) return (Strdup(name)); p--; if ((p <= name) || (!isdigit(*p))) return (Strdup(name)); *(++p) = '\0'; e = Strdup(name); *p = 's'; return (e); } /* * free list of drivenames */ void metafreedrivenamelist( mddrivenamelist_t *dnlp ) { mddrivenamelist_t *next = NULL; for (/* void */; (dnlp != NULL); dnlp = next) { next = dnlp->next; Free(dnlp); } } /* * build list of drivenames */ int metadrivenamelist( mdsetname_t **spp, mddrivenamelist_t **dnlpp, int argc, char *argv[], md_error_t *ep ) { mddrivenamelist_t **tailpp = dnlpp; int count = 0; for (*dnlpp = NULL; (argc > 0); ++count, --argc, ++argv) { mddrivenamelist_t *dnlp = Zalloc(sizeof (*dnlp)); if ((dnlp->drivenamep = metadrivename(spp, argv[0], ep)) == NULL) { metafreedrivenamelist(*dnlpp); *dnlpp = NULL; return (-1); } *tailpp = dnlp; tailpp = &dnlp->next; } return (count); } /* * append to end of drivename list */ mddrivename_t * metadrivenamelist_append( mddrivenamelist_t **dnlpp, mddrivename_t *dnp ) { mddrivenamelist_t *dnlp; /* run to end of list */ for (; (*dnlpp != NULL); dnlpp = &(*dnlpp)->next) ; /* allocate new list element */ dnlp = *dnlpp = Zalloc(sizeof (*dnlp)); /* append drivename */ dnlp->drivenamep = dnp; return (dnp); } /* * FUNCTION: meta_drivenamelist_append_wrapper() * INPUT: tailpp - pointer to the list tail pointer * dnp - name node to be appended to list * OUTPUT: none * RETURNS: mddrivenamelist_t * - new tail of the list. * PURPOSE: wrapper to meta_namelist_append for performance. * metanamelist_append finds the tail each time which slows * down long lists. By keeping track of the tail ourselves * we can change metadrivenamelist_append into a * constant time operation. */ mddrivenamelist_t ** meta_drivenamelist_append_wrapper( mddrivenamelist_t **tailpp, mddrivename_t *dnp ) { (void) metadrivenamelist_append(tailpp, dnp); /* If it's the first item in the list, return it instead of the next */ if ((*tailpp)->next == NULL) return (tailpp); return (&(*tailpp)->next); } /* * mdname_t stuff */ /* * check set and get comparison name */ char * meta_name_getname( mdsetname_t **spp, char *uname, md_error_t *ep ) { char *sname = NULL; int ismeta = 0; unit_t unit; /* check set name */ if (parse_metadevice(uname, &sname, &unit) == 0) ismeta = 1; if (chksetname(spp, sname, ep) != 0) { if (sname != NULL) Free(sname); return (NULL); } if (sname != NULL) Free(sname); /* return comparison name */ if (ismeta) return (canon_metadevice((*spp)->setname, unit)); else return (Strdup(uname)); } /* * FUNCTION: getrname() * INPUT: spp - the setname struct * uname - the possibly unqualified device name * OUTPUT: ep - return error pointer * RETURNS: char* - character string containing the fully * qualified raw device name * PURPOSE: Create the fully qualified raw name for the possibly * unqualified device name. If uname is an absolute * path the raw name is derived from the input string. * Otherwise, an attempt is made to get the rawname by * catting "/dev/md/rdsk" and "/dev/rdsk". */ static char * getrname(mdsetname_t **spp, char *uname, md_error_t *ep) { char *rname, *fname; int constructed = 0; assert(uname != NULL); /* if it is an absolute name then just call rawname on the input */ if (uname[0] == '/') { if ((rname = rawname(uname)) != NULL) return (rname); /* out of luck */ (void) mdsyserror(ep, ENOENT, uname); return (NULL); } /* * Check for metadevice before physical device. * With the introduction of softpartitions it is more * likely to be a metadevice. */ /* metadevice short form */ if (metaislocalset(*spp)) { fname = Malloc(strlen(uname) + strlen("/dev/md/rdsk/") + 1); (void) strcpy(fname, "/dev/md/rdsk/"); (void) strcat(fname, uname); if (*uname == 'd') constructed = 1; } else { char *p; size_t len; if ((p = strchr(uname, '/')) != NULL) { ++p; } else { p = uname; } len = strlen((*spp)->setname) + strlen(p) + strlen("/dev/md//rdsk/") + 1; fname = Malloc(len); (void) snprintf(fname, len, "/dev/md/%s/rdsk/%s", (*spp)->setname, p); if (*p == 'd') constructed = 1; } rname = rawname(fname); /* * Handle the case where we have a new metadevice that does not yet * exist in the name-space. In this case we return the constructed * metadevice name as that will exist after the metainit call has * created it. */ if ((rname == NULL) && constructed) { rname = Strdup(fname); } Free(fname); if (rname != NULL) return (rname); fname = Malloc(strlen(uname) + strlen("/dev/rdsk/") + 1); (void) strcpy(fname, "/dev/rdsk/"); (void) strcat(fname, uname); rname = rawname(fname); Free(fname); if (rname != NULL) return (rname); /* * If all else fails try the straight uname. * NOTE: This check was at the beginning of getrname instead * of here. It was moved to avoid a conflict with SC3.0. If * a diskset was mounted with the same name it would hang * the cluster in a loop. Example: * * fubar/d10 -m fubar/d0 fubar/d1 * mount /dev/md/fubar/dsk/d10 /fubar * * When the system was booted DiskSuite would try to take ownership * of diskset fubar. This would cause rawname("fubar/d10") to be * called. rawname() stats the string which caused the cluster * reservation code to try and take ownership which it was already * doing and a deadlock would occur. By moving this final attempt * at resolving the rawname to the end we avoid this deadlock. */ if (rname = rawname(uname)) return (rname); /* out of luck */ (void) mdsyserror(ep, ENOENT, uname); return (NULL); } /* * get raw slice and drive names */ static char * getrawnames( mdsetname_t **spp, char *uname, char **dnamep, md_error_t *ep ) { char *rname; size_t len; /* initialize */ *dnamep = NULL; /* get slice name */ if ((rname = getrname(spp, uname, ep)) != NULL) { *dnamep = metadiskname(rname); return (rname); } /* * If name cannot be found, if may be because is is not accessible. * If it is an absolute name, try all possible disk name formats and * if it is device name, assume it is /dev/rdsk/... */ if (mdissyserror(ep, ENOENT)) { if (uname[0] == '/') { /* Absolute name */ char *p; uint_t d = 0; int l = 0; char onmb[BUFSIZ+1], snm[BUFSIZ+1]; /* * Handle old style raw names */ if (sscanf(uname, "/dev/r%" VAL2STR(BUFSIZ) "[^0-9/]%u" "%" VAL2STR(BUFSIZ) "[a-h]%n", onmb, &d, snm, &l) == 3 && l == strlen(uname)) { mdclrerror(ep); rname = Strdup(uname); *dnamep = metadiskname(rname); return (rname); } /* * Handle old style block names */ if (sscanf(uname, "/dev/%" VAL2STR(BUFSIZ) "[^0-9/]%u" "%" VAL2STR(BUFSIZ) "[a-h]%n", onmb, &d, snm, &l) == 3 && l == strlen(uname)) { len = strlen(uname) + 1 + 1; rname = Malloc(len); (void) snprintf(rname, len, "/dev/r%s%u%s", onmb, d, snm); *dnamep = metadiskname(rname); return (rname); } /* /.../dsk/... */ if ((p = strstr(uname, "/dsk/")) != NULL) { mdclrerror(ep); ++p; rname = Malloc(strlen(uname) + 1 + 1); (void) strncpy(rname, uname, (p - uname)); rname[(p - uname)] = 'r'; (void) strcpy(&rname[(p - uname) + 1], p); *dnamep = metadiskname(rname); return (rname); } /* /.../rdsk/... */ else if (strstr(uname, "/rdsk/") != NULL) { mdclrerror(ep); rname = Strdup(uname); *dnamep = metadiskname(rname); return (rname); } } else { /* * If it's not an absolute name but is a valid ctd name, * guess at /dev/rdsk/... */ uint_t s; if (parse_ctd(uname, &s) == 0) { len = strlen(uname) + strlen("/dev/rdsk/") + 1; rname = Malloc(len); (void) snprintf(rname, len, "/dev/rdsk/%s", uname); *dnamep = metadiskname(rname); return (rname); } } } /* out of luck */ return (NULL); } /* * get number of slices for name */ static int getnslice( char *rname, char *dname, uint_t *slicep ) { char *srname; uint_t nslice; size_t dl = strlen(dname); size_t rl = strlen(rname); size_t l = 0; size_t len; /* * get our slice number - works only with names that end in s%u - * all others return -1. */ if (dl >= rl || sscanf(&rname[dl], "s%u%n", slicep, &l) != 1 || l != rl || (int)*slicep < 0) { return (-1); } /* * go find how many slices there really are */ len = strlen(dname) + 20 + 1; srname = Malloc(len); for (nslice = 0; /* void */; ++nslice) { struct stat statbuf; /* build slice name */ (void) snprintf(srname, len, "%ss%u", dname, nslice); /* see if it's there */ if ((meta_stat(srname, &statbuf) != 0) || (! S_ISCHR(statbuf.st_mode))) { break; } } Free(srname); /* Need to make sure that we at least have V_NUMPAR */ nslice = max(nslice, V_NUMPAR); /* make sure we have at least our slice */ if (nslice < *slicep) return (-1); /* return number of slices */ return (nslice); } /* * Attempt to parse the input string as a c[t]ds specifier * The target can either be a SCSI target id or if the device * is in a fabric configuration in a fibre channel setup then * the target is a standard WWN (world wide name). * * if successful return 0 * if c[t]dp name return 1 * otherwise return -1 */ int parse_ctd( char *uname, uint_t *slice) { uint_t channel; uint_t target; uint_t device; int has_target = 1; uint_t cl; uint_t target_str_len; char *partial_ctd_str; char *target_str; char *device_start_pos; int l = -1; /* pull off the channel spec and the 't' for the target */ if (sscanf(uname, "c%ut%n", &channel, &l) != 1 || l == -1) { /* check for cds style name */ if (sscanf(uname, "c%ud%n", &channel, &l) != 1 || l == -1) { return (-1); } else { l--; /* we want to be on the 'd' */ has_target = 0; } } partial_ctd_str = uname + l; /* find the beginning of the device specifier */ device_start_pos = strrchr(partial_ctd_str, 'd'); if (device_start_pos == NULL) { return (-1); } /* check to see if it is a ctd with a WWN or SCSI target */ if (has_target) { /* pull off the target and see if it is a WWN */ target_str_len = device_start_pos - partial_ctd_str + 2; target_str = (char *)Malloc(target_str_len+1); (void) strcpy(target_str, "0X"); (void) strncpy(target_str+2, partial_ctd_str, target_str_len - 2); target_str[target_str_len] = '\0'; if (sscanf(target_str, "%x%n", &target, &l) != 1 || l != target_str_len) { Free(target_str); return (-1); } Free(target_str); } /* check the device and slice */ cl = strlen(device_start_pos); if (sscanf(device_start_pos, "d%us%u%n", &device, slice, &l) != 2 || l != cl) { /* check the device and partition */ if (sscanf(device_start_pos, "d%up%u%n", &device, slice, &l) == 2 && l == cl) { return (1); } return (-1); } return (0); } /* * get number of slices for name */ static int uname2sliceno( char *uname, uint_t *slicep, md_error_t *ep ) { uint_t c = 0, t = 0, d = 0; int l = 0, cl = 0; int fd; struct dk_cinfo cinfo; char *p; char *rname = NULL; if (is_metaname(uname)) return (*slicep = 0); if ((p = strrchr(uname, '/')) != NULL) p++; else p = uname; cl = strlen(p); if (parse_ctd(p, slicep) == 0) return (*slicep); else if (sscanf(p, "mc%ut%ud%us%u%n", &c, &t, &d, slicep, &l) == 4 && l == cl) return (*slicep); else if (sscanf(p, "d%us%u%n", &d, slicep, &l) == 2 && l == cl) return (*slicep); /* * If we can't get the slice from the name, then we have to do it the * hard and expensive way. */ if ((rname = rawname(uname)) == NULL) return (-1); /* get controller info */ if ((fd = open(rname, (O_RDONLY|O_NDELAY), 0)) < 0) { Free(rname); return (-1); } if (ioctl(fd, DKIOCINFO, &cinfo) != 0) { int save = errno; if (save == ENOTTY) (void) mddeverror(ep, MDE_NOT_DISK, NODEV64, rname); else (void) mdsyserror(ep, save, rname); Free(rname); (void) close(fd); return (-1); } (void) close(fd); /* sd/ssd bug */ if (cinfo.dki_partition < V_NUMPAR) { Free(rname); return (*slicep = cinfo.dki_partition); } return (mddeverror(ep, MDE_NOT_DISK, NODEV64, rname)); } /* * get partition info */ static int getparts( mddrivename_t *dnp, char *rname, char *dname, uint_t *npartsp, uint_t *partnop, md_error_t *ep ) { int nparts; uint_t partno; mdname_t name; mdvtoc_t *vtocp; /* metadevice */ if (is_metaname(rname)) { dnp->type = MDT_META; nparts = 1; partno = 0; goto gotit; } /* see how many partitions in drive, this is really tricky */ metainitname(&name); name.rname = rname; name.drivenamep = dnp; if ((vtocp = metagetvtoc(&name, TRUE, &partno, ep)) != NULL) { dnp->type = MDT_COMP; nparts = vtocp->nparts; /* partno already setup */ /* dname already setup */ goto gotit; } if ((ep->info.errclass == MDEC_DEV) && (ep->info.md_error_info_t_u.dev_error.errnum == MDE_TOO_MANY_PARTS)) return (-1); /* fallback and try and guess (used to check for just EACCES here) */ if ((dname != NULL) && ((nparts = getnslice(rname, dname, &partno)) > 0)) { dnp->type = MDT_ACCES; if (mdanysyserror(ep)) { dnp->errnum = ep->info.md_error_info_t_u.sys_error.errnum; } else { dnp->errnum = ENOENT; } mdclrerror(ep); /* nparts already setup */ /* partno already setup */ /* dname already setup */ nparts = roundup(nparts, V_NUMPAR); goto gotit; } /* nothing worked */ dnp->type = MDT_UNKNOWN; if (mdissyserror(ep, EACCES)) dnp->type = MDT_ACCES; if (mdanysyserror(ep)) { dnp->errnum = ep->info.md_error_info_t_u.sys_error.errnum; } else { dnp->errnum = ENOENT; } mdclrerror(ep); nparts = V_NUMPAR; if (uname2sliceno(rname, &partno, ep) < 0) { mdclrerror(ep); partno = 0; } /* return success */ gotit: assert(nparts > 0); if (partno >= nparts) return (mdsyserror(ep, ENOENT, rname)); *npartsp = nparts; *partnop = partno; return (0); } /* * get block name */ static int getbname( mdname_t *np, md_error_t *ep ) { char *rname = np->rname; char *bname; /* fully qualified */ assert(rname != NULL); if ((bname = blkname(rname)) != NULL) { if (np->bname) Free(np->bname); np->bname = bname; return (0); } /* out of luck */ return (mdsyserror(ep, ENOENT, rname)); } static void getcname( mdsetname_t *sp, mdname_t *np ) { char *sname = sp->setname; char *bname = np->bname; char *p; size_t len; assert(sname != NULL); assert(bname != NULL); assert(np->drivenamep->type != MDT_FAST_COMP && np->drivenamep->type != MDT_FAST_META); /* regular device */ if ((strncmp(bname, "/dev/dsk/", strlen("/dev/dsk/")) == 0) && (strchr((p = bname + strlen("/dev/dsk/")), '/') == NULL)) { if (np->cname) Free(np->cname); np->cname = Strdup(p); return; } if ((strncmp(bname, "/dev/ap/dsk/", strlen("/dev/ap/dsk/")) == 0) && (strchr((p = bname + strlen("/dev/ap/dsk/")), '/') == NULL)) { if (np->cname) Free(np->cname); np->cname = Strdup(p); return; } if ((strncmp(bname, "/dev/did/dsk/", strlen("/dev/did/dsk/")) == 0) && (strchr((p = bname + strlen("/dev/did/dsk/")), '/') == NULL)) { if (np->cname) Free(np->cname); np->cname = Strdup(p); return; } /* anything else but metadevice */ if (np->drivenamep->type != MDT_META) { if (np->cname) Free(np->cname); np->cname = Strdup(bname); return; } /* metadevice */ p = strrchr(bname, '/'); assert(p != NULL); ++p; if (metaislocalset(sp)) { if (np->cname) Free(np->cname); np->cname = Strdup(p); } else { assert(sname[0] != '\0'); if (np->cname) Free(np->cname); len = strlen(sname) + 1 + strlen(p) + 1; np->cname = Malloc(len); (void) snprintf(np->cname, len, "%s/%s", sname, p); } } /* * get dev */ int meta_getdev( mdsetname_t *sp, mdname_t *np, md_error_t *ep ) { struct stat statbuf; /* get dev */ if (meta_stat(np->rname, &statbuf) != 0) return (mdsyserror(ep, errno, np->rname)); else if (! S_ISCHR(statbuf.st_mode)) return (mddeverror(ep, MDE_NOT_DISK, NODEV64, np->rname)); np->dev = meta_expldev(statbuf.st_rdev); assert(np->drivenamep->type != MDT_FAST_META && np->drivenamep->type != MDT_FAST_COMP); /* check set */ assert((np->drivenamep->type == MDT_META) ? (sp->setno == MD_MIN2SET(meta_getminor(np->dev))) : 1); /* return sucess */ return (0); } /* * set up names for a slice */ static int getnames( mdsetname_t *sp, mdname_t *np, char *rname, md_error_t *ep ) { /* get names */ if (np->rname) Free(np->rname); np->rname = Strdup(rname); if (getbname(np, ep) != 0) return (-1); getcname(sp, np); if (meta_getdev(sp, np, ep) != 0) return (-1); /* return success */ return (0); } /* * fake up names for a slice */ static void getfakenames( mdsetname_t *sp, mdname_t *np, char *rname ) { char *p; char onmb[BUFSIZ+1], snm[BUFSIZ+1]; uint_t d = 0; int l = 0; /* fake names */ if (np->rname != NULL) Free(np->rname); np->rname = Strdup(rname); if (np->bname != NULL) Free(np->bname); np->bname = Strdup(rname); /* * Fixup old style names */ if (sscanf(rname, "/dev/r%" VAL2STR(BUFSIZ) "[^0-9/]%u" "%" VAL2STR(BUFSIZ) "[a-h]%n", onmb, &d, snm, &l) == 3 && l == strlen(rname)) (void) snprintf(np->bname, l, "/dev/%s%u%s", onmb, d, snm); /* * Fixup new style names */ if ((p = strstr(np->bname, "/rdsk/")) != NULL) { for (++p; (*(p + 1) != '\0'); ++p) *p = *(p + 1); *p = '\0'; } if (np->cname != NULL) Free(np->cname); getcname(sp, np); } static mdname_t * setup_slice( mdsetname_t *sp, mddrivename_t *dnp, char *uname, char *rname, char *dname, uint_t partno, md_error_t *ep ) { char *srname = NULL; mdname_t *np; /* must have a set */ assert(sp != NULL); assert(partno < dnp->parts.parts_len); assert(dname != NULL); np = &dnp->parts.parts_val[partno]; if (rname) srname = rname; else if (is_metaname(dname)) srname = dname; else { char onmb[BUFSIZ+1]; uint_t d = 0; int l = 0, cl = strlen(dname); size_t len; len = cl + 20 + 1; srname = Malloc(len); /* * Handle /dev/rXXNN. */ if (sscanf(dname, "/dev/r%" VAL2STR(BUFSIZ) "[^0-9/]%u%n", onmb, &d, &l) == 2 && l == cl) { (void) snprintf(srname, len, "/dev/r%s%u%c", onmb, d, 'a' + partno); } else if (sscanf(dname, "/dev/%" VAL2STR(BUFSIZ) "[^0-9/]%u%n", onmb, &d, &l) == 2 && l == cl) { (void) snprintf(srname, len, "/dev/%s%u%c", onmb, d, 'a' + partno); } else { /* build the slice that is wanted */ (void) snprintf(srname, len, "%ss%u", dname, partno); } } if (getnames(sp, np, srname, ep) != 0) { if (dnp->type == MDT_UNKNOWN) { mdclrerror(ep); getfakenames(sp, np, srname); } else if (dnp->type == MDT_COMP && mdissyserror(ep, ENOENT)) { dnp->type = MDT_UNKNOWN; if (mdanysyserror(ep)) { dnp->errnum = ep->info.md_error_info_t_u.sys_error.errnum; } else { dnp->errnum = ENOENT; } mdclrerror(ep); getfakenames(sp, np, srname); } else { mdclrerror(ep); if (getnames(sp, np, dname, ep) != 0) { np = NULL; goto fixup; } } } out: if ((srname != rname) && (srname != dname)) Free(srname); /* return name */ return (np); fixup: if (mdanysyserror(ep)) { char *p; int errnum = ep->info.md_error_info_t_u.sys_error.errnum; mdclrerror(ep); if (uname && *uname) { if ((p = strrchr(uname, '/')) != NULL) (void) mdsyserror(ep, errnum, ++p); else (void) mdsyserror(ep, errnum, uname); } else { if ((p = strrchr(srname, '/')) != NULL) (void) mdsyserror(ep, errnum, ++p); else (void) mdsyserror(ep, errnum, srname); } } goto out; } /* * flush the fast name cache */ static void metafreefastnm(mdname_t **np) { mddrivename_t *dnp; assert(np != NULL && *np != NULL); if ((dnp = (*np)->drivenamep) != NULL) { if (dnp->cname != NULL) Free(dnp->cname); if (dnp->rname != NULL) Free(dnp->rname); if (dnp->miscname != NULL) Free(dnp->miscname); meta_free_unit(dnp); Free(dnp); } if ((*np)->cname != NULL) Free((*np)->cname); if ((*np)->bname != NULL) Free((*np)->bname); if ((*np)->rname != NULL) Free((*np)->rname); if ((*np)->devicesname != NULL) Free((*np)->devicesname); Free(*np); *np = NULL; } /* * flush the fast name cache */ static void metaflushfastnames() { mdnamelist_t *p, *n; for (p = fastnmlp, n = NULL; (p != NULL); p = n) { n = p->next; metafreefastnm(&p->namep); Free(p); } fastnmlp = NULL; } static char * getrname_fast(char *unm, md_error_t *ep) { uint_t d = 0; int l = 0; int cl = strlen(unm); char onmb[BUFSIZ+1], snm[BUFSIZ+1], cnmb[BUFSIZ]; char *rnm; char *p; size_t len; if (is_metaname(unm)) { /* without set */ if (sscanf(unm, "d%u%n", &d, &l) == 1 && cl == l) { rnm = Zalloc(14 + cl + 1); (void) sprintf(rnm, "/dev/md/rdsk/d%u", d); return (rnm); } /* fully-qualified without set */ if ((sscanf(unm, "/dev/md/dsk/d%u%n", &d, &l) == 1 || sscanf(unm, "/dev/md/rdsk/d%u%n", &d, &l) == 1) && cl == l) { rnm = Zalloc(14 + cl + 1); (void) sprintf(rnm, "/dev/md/rdsk/d%u", d); return (rnm); } /* with set */ if ((sscanf(unm, "%" VAL2STR(BUFSIZ) "[^/]/d%u%n", snm, &d, &l) == 2 || sscanf(unm, "/dev/md/%" VAL2STR(BUFSIZ) "[^/]/dsk/d%u%n", snm, &d, &l) == 2 || sscanf(unm, "/dev/md/%" VAL2STR(BUFSIZ) "[^/]/rdsk/d%u%n", snm, &d, &l) == 2) && cl == l) { len = 14 + cl + strlen(snm) + 1; rnm = Zalloc(len); (void) snprintf(rnm, len, "/dev/md/%s/rdsk/d%u", snm, d); return (rnm); } } /* NOT Fully qualified path, done */ if (unm[0] != '/') { (void) mdsyserror(ep, EINVAL, unm); return (NULL); } /* * Get slice information from old style names of the form * /dev/rXXNN[a-h] or /dev/XXNN[a-h], must be done before regular * devices, but after metadevices. */ if ((sscanf(unm, "/dev/r%" VAL2STR(BUFSIZ) "[^0-9/]%u" "%" VAL2STR(BUFSIZ) "[a-h]%n", onmb, &d, snm, &l) == 3 || sscanf(unm, "/dev/%" VAL2STR(BUFSIZ) "[^0-9/]%u" "%" VAL2STR(BUFSIZ) "[a-h]%n", onmb, &d, snm, &l) == 3) && l == cl) { if ((p = strchr("abcdefgh", snm[0])) != NULL) { (void) snprintf(cnmb, sizeof (cnmb), "/dev/r%s%u%s", onmb, d, snm); return (Strdup(cnmb)); } } if ((p = strstr(unm, "/dsk/")) != NULL) { /* /.../dsk/... */ ++p; rnm = Zalloc(strlen(unm) + 1 + 1); (void) strncpy(rnm, unm, (p - unm)); rnm[(p - unm)] = 'r'; (void) strcpy(&rnm[(p - unm) + 1], p); return (rnm); } else if (strstr(unm, "/rdsk/") != NULL) { /* /.../rdsk/... */ return (Strdup(unm)); } /* * Shouldn't get here but if we do then we have an unrecognized * fully qualified path - error */ (void) mdsyserror(ep, EINVAL, unm); return (NULL); } static mdname_t * metainitfastname( mdsetname_t *sp, char *uname, md_error_t *ep ) { uint_t c = 0, t = 0, d = 0, s = 0; int l = 0; mddrivename_t *dnp; mdname_t *np; mdnamelist_t **fnlpp; for (fnlpp = &fastnmlp; (*fnlpp != NULL); fnlpp = &(*fnlpp)->next) { np = (*fnlpp)->namep; if (strcmp(np->bname, uname) == 0) return (np); } *fnlpp = Zalloc(sizeof (**fnlpp)); np = (*fnlpp)->namep = Zalloc(sizeof (mdname_t)); metainitname(np); dnp = np->drivenamep = Zalloc(sizeof (mddrivename_t)); metainitdrivename(dnp); /* Metadevices */ if (is_metaname(uname)) { char *p; size_t len; if ((p = strrchr(uname, '/')) != NULL) ++p; else p = uname; if (metaislocalset(sp)) { if (np->cname) Free(np->cname); np->cname = Strdup(p); } else { if (np->cname) Free(np->cname); len = strlen(sp->setname) + 1 + strlen(p) + 1; np->cname = Zalloc(len); (void) snprintf(np->cname, len, "%s/%s", sp->setname, p); } dnp->type = MDT_FAST_META; goto done; } /* Others */ dnp->type = MDT_FAST_COMP; if (((sscanf(uname, "/dev/rdsk/c%ut%ud%us%u%n", &c, &t, &d, &s, &l) == 4 || sscanf(uname, "/dev/dsk/c%ut%ud%us%u%n", &c, &t, &d, &s, &l) == 4 || sscanf(uname, "/dev/ap/rdsk/mc%ut%ud%us%u%n", &c, &t, &d, &s, &l) == 4 || sscanf(uname, "/dev/ap/dsk/mc%ut%ud%us%u%n", &c, &t, &d, &s, &l) == 4 || sscanf(uname, "/dev/did/rdsk/d%us%u%n", &t, &s, &l) == 2 || sscanf(uname, "/dev/did/dsk/d%us%u%n", &t, &s, &l) == 2|| sscanf(uname, "/dev/rdsk/c%ud%us%u%n", &c, &d, &s, &l) == 3 || sscanf(uname, "/dev/dsk/c%ud%us%u%n", &c, &d, &s, &l) == 3 || sscanf(uname, "/dev/rdsk/c%ut%ud%u%n", &c, &t, &d, &l) == 3 || sscanf(uname, "/dev/dsk/c%ut%ud%u%n", &c, &t, &d, &l) == 3 || sscanf(uname, "/dev/ap/rdsk/mc%ut%ud%u%n", &c, &t, &d, &l) == 3 || sscanf(uname, "/dev/ap/dsk/mc%ut%ud%u%n", &c, &t, &d, &l) == 3 || sscanf(uname, "/dev/did/rdsk/d%u%n", &t, &l) == 1 || sscanf(uname, "/dev/did/dsk/d%u%n", &t, &l) == 1 || sscanf(uname, "/dev/rdsk/c%ud%u%n", &c, &d, &l) == 2 || sscanf(uname, "/dev/dsk/c%ud%u%n", &c, &d, &l) == 2) && l == strlen(uname))) { if ((np->cname = strrchr(uname, '/')) == NULL) np->cname = Strdup(uname); else np->cname = Strdup(++np->cname); } else { np->cname = Strdup(uname); } done: /* Driver always gives us block names */ np->bname = Strdup(uname); /* canonical disk name */ if ((dnp->cname = metadiskname(np->cname)) == NULL) dnp->cname = Strdup(np->cname); if ((np->rname = getrname_fast(uname, ep)) != NULL) { if ((dnp->rname = metadiskname(np->rname)) == NULL) dnp->rname = Strdup(np->rname); } else { metafreefastnm(&(*fnlpp)->namep); Free(*fnlpp); *fnlpp = NULL; return (NULL); } /* cleanup, return success */ return (np); } /* * set up names for a device */ static mdname_t * metaname_common( mdsetname_t **spp, char *uname, int fast, md_error_t *ep ) { mddrivenamelist_t **tail; mddrivename_t *dnp; uint_t slice; mdname_t *np; char *rname = NULL; char *dname = NULL; char *cname = NULL; uint_t nparts, partno; assert(uname != NULL); /* check setname */ if ((cname = meta_name_getname(spp, uname, ep)) == NULL) return (NULL); assert(*spp != NULL); Free(cname); /* get raw name (rname) of the slice and drive (dname) we have */ if ((rname = getrawnames(spp, uname, &dname, ep)) == NULL) { return (NULL); } /* look in cache first */ for (tail = &drivelistp; (*tail != NULL); tail = &(*tail)->next) { dnp = (*tail)->drivenamep; /* check to see if the drive name is already in the cache */ if ((dnp->rname != NULL) && strcmp(dnp->rname, dname) == 0) { Free(rname); if (dname != NULL) Free(dname); if (uname2sliceno(uname, &partno, ep) < 0) return (NULL); return (metaslicename(dnp, partno, ep)); } } /* * If a fast names is OK, then get one, and be done. */ if (fast) { Free(rname); if (dname != NULL) Free(dname); return (metainitfastname(*spp, uname, ep)); } /* allocate new list element and drive */ *tail = Zalloc(sizeof (**tail)); dnp = (*tail)->drivenamep = Zalloc(sizeof (*dnp)); metainitdrivename(dnp); /* get parts info */ if (getparts(dnp, rname, dname, &nparts, &partno, ep) != 0) goto out; /* * libmeta needs at least V_NUMPAR partitions. * If we have an EFI partition with less than V_NUMPAR slices, * we nevertheless reserve space for V_NUMPAR */ if (nparts < V_NUMPAR) { nparts = V_NUMPAR; } /* allocate and link in parts */ dnp->parts.parts_len = nparts; dnp->parts.parts_val = Zalloc((sizeof (*dnp->parts.parts_val)) * dnp->parts.parts_len); for (slice = 0; (slice < nparts); ++slice) { np = &dnp->parts.parts_val[slice]; metainitname(np); np->drivenamep = dnp; } /* setup name_t (or slice) wanted */ if ((np = setup_slice(*spp, dnp, uname, rname, dname, partno, ep)) == NULL) goto out; /* canonical disk name */ if ((dnp->cname = metadiskname(np->cname)) == NULL) dnp->cname = Strdup(np->cname); if ((dnp->rname = metadiskname(np->rname)) == NULL) dnp->rname = Strdup(np->rname); /* cleanup, return success */ if (dname != NULL) Free(dname); Free(rname); return (np); /* cleanup, return error */ out: if (dname != NULL) Free(dname); if (rname != NULL) Free(rname); metafreedrivename(dnp); Free(dnp); Free(*tail); *tail = NULL; return (NULL); } mdname_t * metaname( mdsetname_t **spp, char *uname, md_error_t *ep ) { return (metaname_common(spp, uname, 0, ep)); } mdname_t * metaname_fast( mdsetname_t **spp, char *uname, md_error_t *ep ) { return (metaname_common(spp, uname, 1, ep)); } /* * set up names for a drive */ mddrivename_t * metadrivename( mdsetname_t **spp, char *uname, md_error_t *ep ) { char *slicename; mdname_t *np; char *cname; mddrivenamelist_t **tail; mddrivename_t *dnp; char *dname; int i; int mplen; size_t len; /* check setname, get comparison name */ assert(uname != NULL); if ((cname = meta_name_getname(spp, uname, ep)) == NULL) { (void) mdsyserror(ep, ENOENT, uname); return (NULL); } assert(*spp != NULL); if ((dname = metadiskname(cname)) == NULL) { (void) mdsyserror(ep, ENOENT, cname); Free(cname); return (NULL); } /* look in cache first */ for (tail = &drivelistp; (*tail != NULL); tail = &(*tail)->next) { dnp = (*tail)->drivenamep; if ((dnp->cname != NULL && (strcmp(dnp->cname, dname) == 0)) || (dnp->rname != NULL && (strcmp(dnp->rname, dname) == 0))) { Free(cname); Free(dname); return (dnp); } } /* Check each possible slice name based on MD_MAX_PARTS. */ /* * Figure out how much string space to reserve to fit * (MD_MAX_PARTS - 1) into the name string; the loop will * increment the mplen counter once for each decimal digit in * (MD_MAX_PARTS - 1). */ for (i = MD_MAX_PARTS - 1, mplen = 0; i; i /= 10, ++mplen); len = strlen(uname) + mplen + 2; slicename = Malloc(len); /* Check for each slice in turn until we find one */ for (np = NULL, i = 0; ((np == NULL) && (i < MD_MAX_PARTS)); ++i) { (void) snprintf(slicename, len, "%ss%d", uname, i); np = metaname(spp, slicename, ep); } Free(slicename); if (np == NULL) { char *dname; if ((mdissyserror(ep, ENOENT)) && ((dname = metadiskname(uname)) != NULL)) { Free(dname); (void) mderror(ep, MDE_NOT_DRIVENAME, uname); } return (NULL); } return (np->drivenamep); } /* * FUNCTION: metaslicename() * INPUT: dnp - the drivename structure * sliceno - the slice on the drive to return * OUTPUT: ep - return error pointer * RETURNS: mdname_t- pointer the the slice name structure * PURPOSE: interface to the parts struct in the drive name struct * Since there is no guarantee that the slice name * structures are populated users should call this * function rather than accessing the structure directly * since it will populate the structure values if they * haven't already been populated before returning. */ mdname_t * metaslicename( mddrivename_t *dnp, uint_t sliceno, md_error_t *ep ) { mdsetname_t *sp = NULL; char *namep = NULL; mdname_t *np; assert(dnp->type != MDT_FAST_COMP && dnp->type != MDT_FAST_META); if (sliceno >= dnp->parts.parts_len) { (void) mderror(ep, MDE_NOSLICE, dnp->cname); return (NULL); } np = &dnp->parts.parts_val[sliceno]; /* check to see if the struct is already populated */ if (np->cname) { return (np); } if ((namep = meta_name_getname(&sp, dnp->cname, ep)) == NULL) return (NULL); np = setup_slice(sp, dnp, NULL, NULL, dnp->rname, sliceno, ep); Free(namep); return (np); } /* * set up metadevice name from id */ mdname_t * metamnumname( mdsetname_t **spp, minor_t mnum, int fast, md_error_t *ep ) { set_t setno = MD_MIN2SET(mnum); mdsetname_t *sp = NULL; char *uname; mdname_t *np; size_t len; /* check set first */ if (spp == NULL) spp = &sp; if (chksetno(spp, setno, ep) != 0) return (NULL); assert(*spp != NULL); sp = *spp; /* build corresponding device name */ if (metaislocalset(sp)) { uname = Malloc(20); (void) sprintf(uname, "d%lu", MD_MIN2UNIT(mnum)); } else { len = strlen(sp->setname) + 1 + 20; uname = Malloc(len); (void) snprintf(uname, len, "%s/d%lu", sp->setname, MD_MIN2UNIT(mnum)); } /* setup name */ if (fast) { np = metaname_fast(spp, uname, ep); np->dev = metamakedev(mnum); } else np = metaname(spp, uname, ep); Free(uname); return (np); } /* * return metadevice name */ char * get_mdname( minor_t mnum ) { mdname_t *np; md_error_t status = mdnullerror; /* get name */ if ((np = metamnumname(NULL, mnum, 0, &status)) == NULL) { mdclrerror(&status); return (NULL); } assert(meta_getminor(np->dev) == mnum); /* return name */ return (np->cname); } /* * check for device type */ int metaismeta( mdname_t *np ) { return (np->drivenamep->type == MDT_META || np->drivenamep->type == MDT_FAST_META); } int metachkmeta( mdname_t *np, md_error_t *ep ) { if (! metaismeta(np)) { return (mddeverror(ep, MDE_NOT_META, np->dev, np->cname)); } return (0); } int metachkdisk( mdname_t *np, md_error_t *ep ) { mddrivename_t *dnp = np->drivenamep; assert(dnp->type != MDT_FAST_COMP && dnp->type != MDT_FAST_META); if ((! metaismeta(np)) && (dnp->type != MDT_COMP)) { switch (dnp->type) { case MDT_ACCES: case MDT_UNKNOWN: return (mdsyserror(ep, dnp->errnum, np->bname)); default: assert(0); return (mddeverror(ep, MDE_NOT_DISK, np->dev, np->cname)); } } return (0); } int metachkcomp( mdname_t *np, md_error_t *ep ) { if (metaismeta(np)) { return (mddeverror(ep, MDE_IS_META, np->dev, np->cname)); } return (metachkdisk(np, ep)); } /* * free list of names */ void metafreenamelist( mdnamelist_t *nlp ) { mdnamelist_t *next = NULL; for (/* void */; (nlp != NULL); nlp = next) { next = nlp->next; Free(nlp); } } /* * build list of names */ int metanamelist( mdsetname_t **spp, mdnamelist_t **nlpp, int argc, char *argv[], md_error_t *ep ) { mdnamelist_t **tailpp = nlpp; int count = 0; for (*nlpp = NULL; (argc > 0); ++count, --argc, ++argv) { mdnamelist_t *nlp = Zalloc(sizeof (*nlp)); if ((nlp->namep = metaname(spp, argv[0], ep)) == NULL) { metafreenamelist(*nlpp); *nlpp = NULL; return (-1); } *tailpp = nlp; tailpp = &nlp->next; } return (count); } /* * append to end of name list */ mdname_t * metanamelist_append( mdnamelist_t **nlpp, mdname_t *np ) { mdnamelist_t *nlp; /* run to end of list */ for (; (*nlpp != NULL); nlpp = &(*nlpp)->next) ; /* allocate new list element */ nlp = *nlpp = Zalloc(sizeof (*nlp)); /* append name */ nlp->namep = np; return (np); } /* * FUNCTION: meta_namelist_append_wrapper() * INPUT: tailpp - pointer to the list tail pointer * np - name node to be appended to list * OUTPUT: none * RETURNS: mdnamelist_t * - new tail of the list. * PURPOSE: wrapper to meta_namelist_append for performance. * metanamelist_append finds the tail each time which slows * down long lists. By keeping track of the tail ourselves * we can change metanamelist_append into a constant time * operation. */ mdnamelist_t ** meta_namelist_append_wrapper( mdnamelist_t **tailpp, mdname_t *np ) { (void) metanamelist_append(tailpp, np); /* If it's the first item in the list, return it instead of the next */ if ((*tailpp)->next == NULL) return (tailpp); return (&(*tailpp)->next); } /* * mdhspname_t stuff */ /* * initialize hspname */ static void metainithspname( mdhspname_t *hspnamep ) { (void) memset(hspnamep, '\0', sizeof (*hspnamep)); hspnamep->hsp = MD_HSP_NONE; } /* * free allocated hspname */ static void metafreehspname( mdhspname_t *hspnamep ) { if (hspnamep->hspname != NULL) Free(hspnamep->hspname); if (hspnamep->unitp != NULL) meta_invalidate_hsp(hspnamep); metainithspname(hspnamep); } /* * clear the hspname cache */ static void metaflushhspnames() { mdhspnamelist_t *p, *n; for (p = hsplistp, n = NULL; (p != NULL); p = n) { n = p->next; metafreehspname(p->hspnamep); Free(p->hspnamep); Free(p); } hsplistp = NULL; } /* * check set and get comparison name */ static char * gethspname( mdsetname_t **spp, char *uname, hsp_t *hspp, md_error_t *ep ) { char *sname = NULL; /* check setname */ assert(uname != NULL); if (parse_hsp(uname, &sname, hspp) != 0) { (void) mdsyserror(ep, ENOENT, uname); return (NULL); } if (chksetname(spp, sname, ep) != 0) { if (sname != NULL) Free(sname); return (NULL); } if (sname != NULL) Free(sname); /* return comparison name */ return (canon_hsp((*spp)->setname, *hspp)); } /* * set up names for a hotspare pool */ mdhspname_t * metahspname( mdsetname_t **spp, char *uname, md_error_t *ep ) { char *cname; hsp_t hsp; mdhspnamelist_t **tail; mdhspname_t *hspnp; /* check setname */ assert(uname != NULL); if ((cname = gethspname(spp, uname, &hsp, ep)) == NULL) return (NULL); assert(*spp != NULL); /* look in cache first */ for (tail = &hsplistp; (*tail != NULL); tail = &(*tail)->next) { hspnp = (*tail)->hspnamep; if (strcmp(hspnp->hspname, cname) == 0) { Free(cname); return (hspnp); } } /* allocate new list element and hspname */ *tail = Zalloc(sizeof (**tail)); hspnp = (*tail)->hspnamep = Zalloc(sizeof (*hspnp)); metainithspname(hspnp); /* save hspname and number */ hspnp->hspname = cname; hspnp->hsp = MAKE_HSP_ID((*spp)->setno, hsp); /* success */ return (hspnp); /* cleanup, return error */ out: metafreehspname(hspnp); Free(hspnp); Free(*tail); *tail = NULL; return (NULL); } /* * set up hotspare pool name from id */ mdhspname_t * metahsphspname( mdsetname_t **spp, hsp_t hsp, md_error_t *ep ) { set_t setno = HSP_SET(hsp); mdsetname_t *sp = NULL; char *uname; mdhspname_t *hspnp; size_t len; /* check set first */ if (spp == NULL) spp = &sp; if (chksetno(spp, setno, ep) != 0) return (NULL); assert(*spp != NULL); sp = *spp; /* build corresponding hotspare pool name */ if (metaislocalset(sp)) { uname = Malloc(20); (void) sprintf(uname, "hsp%03u", HSP_ID(hsp)); } else { len = strlen(sp->setname) + 1 + 20; uname = Malloc(len); (void) snprintf(uname, len, "%s/hsp%03lu", sp->setname, HSP_ID(hsp)); } /* setup name */ hspnp = metahspname(spp, uname, ep); Free(uname); return (hspnp); } /* * return hotspare pool name */ char * get_hspname(hsp_t hsp) { mdhspname_t *hspnp; md_error_t status = mdnullerror; /* get name */ if ((hspnp = metahsphspname(NULL, hsp, &status)) == NULL) { mdclrerror(&status); return (NULL); } /* return name */ return (hspnp->hspname); } /* * free hotspare pool list */ void metafreehspnamelist(mdhspnamelist_t *hspnlp) { mdhspnamelist_t *next = NULL; for (/* void */; (hspnlp != NULL); hspnlp = next) { next = hspnlp->next; Free(hspnlp); } } /* * build list of hotspare pool names */ int metahspnamelist( mdsetname_t **spp, mdhspnamelist_t **hspnlpp, int argc, char *argv[], md_error_t *ep ) { mdhspnamelist_t **tailpp = hspnlpp; int count = 0; for (*hspnlpp = NULL; (argc > 0); ++count, --argc, ++argv) { mdhspnamelist_t *hspnlp = Zalloc(sizeof (*hspnlp)); if ((hspnlp->hspnamep = metahspname(spp, argv[0], ep)) == NULL) { metafreehspnamelist(*hspnlpp); *hspnlpp = NULL; return (-1); } *tailpp = hspnlp; tailpp = &hspnlp->next; } return (count); } /* * append to end of hotspare pool list */ mdhspname_t * metahspnamelist_append(mdhspnamelist_t **hspnlpp, mdhspname_t *hspnp) { mdhspnamelist_t *hspnlp; /* run to end of list */ for (; (*hspnlpp != NULL); hspnlpp = &(*hspnlpp)->next) ; /* allocate new list element */ hspnlp = *hspnlpp = Zalloc(sizeof (*hspnlp)); /* append hotspare pool name */ hspnlp->hspnamep = hspnp; return (hspnp); } /* * get name from dev */ mdname_t * metadevname( mdsetname_t **spp, md_dev64_t dev, md_error_t *ep) { char *device_name; mdname_t *namep; mdkey_t key; /* short circuit metadevices */ assert(dev != NODEV64); if (meta_dev_ismeta(dev)) return (metamnumname(spp, meta_getminor(dev), 0, ep)); /* create local set, if necessary */ if (*spp == NULL) { if ((*spp = metasetname(MD_LOCAL_NAME, ep)) == NULL) return (NULL); } /* get name from namespace */ if ((device_name = meta_getnmentbydev((*spp)->setno, MD_SIDEWILD, dev, NULL, NULL, &key, ep)) == NULL) { return (NULL); } namep = metaname_fast(spp, device_name, ep); if (namep != NULL) namep->key = key; Free(device_name); return (namep); } /* * return cached name from md_dev64_t */ static char * metadevtocachename(md_dev64_t dev) { mddrivenamelist_t *dnlp; /* look in cache */ for (dnlp = drivelistp; (dnlp != NULL); dnlp = dnlp->next) { mddrivename_t *dnp = dnlp->drivenamep; uint_t i; for (i = 0; (i < dnp->parts.parts_len); ++i) { mdname_t *np = &dnp->parts.parts_val[i]; if (np->dev == dev) return (np->cname); } } /* not found */ return (NULL); } /* * Ask the driver for the name, which has been stored in the * metadevice state database (on behalf of the utilities). * (by devno) */ char * get_devname( set_t setno, md_dev64_t dev) { mdsetname_t *sp; mdname_t *np; md_error_t status = mdnullerror; /* get name */ if ((setno == MD_SET_BAD) || ((sp = metasetnosetname(setno, &status)) == NULL) || ((np = metadevname(&sp, dev, &status)) == NULL)) { mdclrerror(&status); return (metadevtocachename(dev)); } /* return name */ return (np->cname); } /* * get name from key */ mdname_t * metakeyname( mdsetname_t **spp, mdkey_t key, int fast, md_error_t *ep ) { char *device_name; md_dev64_t dev = NODEV64; mdname_t *namep; /* create local set, if necessary */ if (*spp == NULL) { if ((*spp = metasetname(MD_LOCAL_NAME, ep)) == NULL) return (NULL); } /* get name from namespace */ if ((device_name = meta_getnmentbykey((*spp)->setno, MD_SIDEWILD, key, NULL, NULL, &dev, ep)) == NULL) { return (NULL); } if (fast) namep = metaname_fast(spp, device_name, ep); else namep = metaname(spp, device_name, ep); assert(dev != NODEV64); if (namep) namep->dev = dev; Free(device_name); return (namep); } /* * completely flush the caches */ void metaflushnames(int flush_sr_cache) { metaflushhspnames(); metaflushdrivenames(); metaflushsetnames(); metaflushctlrcache(); metaflushfastnames(); metaflushstatcache(); if (flush_sr_cache) sr_cache_flush(0); } /* * meta_get_hotspare_names * returns an mdnamelist_t of hot spare names */ int meta_get_hotspare_names( mdsetname_t *sp, mdnamelist_t **nlpp, int options, md_error_t *ep ) { mdhspnamelist_t *hspnlp = NULL; mdhspnamelist_t *hspp; int cnt = 0; assert(nlpp != NULL); /* get hotspare names */ if (meta_get_hsp_names(sp, &hspnlp, options, ep) < 0) { cnt = -1; goto out; } /* build name list */ for (hspp = hspnlp; (hspp != NULL); hspp = hspp->next) { md_hsp_t *hsp; int i; if ((hsp = meta_get_hsp(sp, hspp->hspnamep, ep)) == NULL) { cnt = -1; goto out; } for (i = 0; (i < hsp->hotspares.hotspares_len); i++) { md_hs_t *hs = &hsp->hotspares.hotspares_val[i]; (void) metanamelist_append(nlpp, hs->hsnamep); ++cnt; } } /* cleanup and return count or error */ out: metafreehspnamelist(hspnlp); if ((cnt == -1) && mdisok(ep)) { /* * At least try to give some sort of meaningful error */ (void) mderror(ep, MDE_NO_HSPS, "Generic Hotspare Error"); } return (cnt); } /* * meta_create_non_dup_list * INPUT: mdnp mdname_t pointer to add to the list if a new name * ldevidp list of non-duplicate names. * OUTPUT: ldevidp list of non-duplicate names. * meta_create_non_dup_list will take a mdname_t pointer and if the device * is not in the list (ldevidp) will add it to the list. * User needs to free allocated memory. */ void meta_create_non_dup_list( mdname_t *mdnp, mddevid_t **ldevidpp ) { char *lcname; mddevid_t *tmp; mddevid_t *lastdevidp; mddevid_t *lldevidp; char *ctd, *slice; mddevid_t *ldevidp; if (mdnp == NULL) return; ldevidp = *ldevidpp; /* * Grab the name of the device and strip off slice information */ lcname = Strdup(mdnp->cname); if (lcname == NULL) { return; } ctd = strrchr(lcname, '/'); if (ctd != NULL) slice = strrchr(ctd, 's'); else slice = strrchr(lcname, 's'); if (slice != NULL) *slice = '\0'; if (ldevidp == NULL) { /* first item in list */ ldevidp = Zalloc(sizeof (mddevid_t)); ldevidp->ctdname = lcname; ldevidp->key = mdnp->key; *ldevidpp = ldevidp; } else { for (tmp = ldevidp; (tmp != NULL); tmp = tmp->next) { if (strcmp(tmp->ctdname, lcname) == 0) { /* already there so just return */ Free(lcname); return; } lastdevidp = tmp; } lldevidp = Zalloc(sizeof (mddevid_t)); lldevidp->ctdname = lcname; lldevidp->key = mdnp->key; lastdevidp->next = lldevidp; } }