/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * 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 /* * check componets */ #include #include "meta_lib_prv.h" #include #include #include #include /* possible returns from meta_check_samedrive */ #define CANT_TELL -1 #define NOT_SAMEDRIVE 0 #define IDENTICAL_NAME_DEVT 1 #define IDENTICAL_DEVIDS 2 /* * static list(s) */ typedef struct dev_list { char *dev_name; ddi_devid_t devid; struct dev_list *dev_nxt; } dev_list_t; static dev_list_t *devnamelist = NULL; static char *skip_these_mntents[] = { "nfs", "autofs", "proc", "tmpfs", "cachefs", "rfs", "fd", "mntfs", "lofs", "devfs", "dev", "ctfs", "objfs", "sharefs", NULL }; /* * free swap info */ static void free_swapinfo( struct swaptable *swtp ) { int i; if (swtp == NULL) return; for (i = 0; (i < swtp->swt_n); ++i) { if (swtp->swt_ent[i].ste_path != NULL) Free(swtp->swt_ent[i].ste_path); } Free(swtp); } /* * get swap info */ static int get_swapinfo( struct swaptable **swtpp, int *nswap, md_error_t *ep ) { int i; size_t swtsize; *swtpp = NULL; /* get number of entries */ if ((*nswap = swapctl(SC_GETNSWP, NULL)) < 0) { return (mdsyserror(ep, errno, "swapctl(SC_GETNSWP)")); } /* allocate structure */ swtsize = sizeof ((*swtpp)->swt_n) + ((*nswap) * sizeof ((*swtpp)->swt_ent[0])); *swtpp = (struct swaptable *)Zalloc(swtsize); (*swtpp)->swt_n = *nswap; for (i = 0; (i < (*nswap)); ++i) (*swtpp)->swt_ent[i].ste_path = Zalloc(MAXPATHLEN); /* get info */ if (((*nswap) = swapctl(SC_LIST, (*swtpp))) < 0) { (void) mdsyserror(ep, errno, "swapctl(SC_LIST)"); free_swapinfo(*swtpp); return (-1); } /* return success */ return (0); } /* * check whether device is swapped on */ static int meta_check_swapped( mdsetname_t *sp, mdname_t *np, md_error_t *ep ) { struct swaptable *swtp; int nswap; int i; int rval = 0; /* should have a set */ assert(sp != NULL); /* get swap info */ if (get_swapinfo(&swtp, &nswap, ep) != 0) return (-1); /* look for match */ for (i = 0; ((i < nswap) && (rval == 0)); ++i) { mdname_t *snp; if ((snp = metaname(&sp, swtp->swt_ent[i].ste_path, UNKNOWN, ep)) == NULL) { mdclrerror(ep); continue; } if (np->dev == snp->dev) { rval = mddeverror(ep, MDE_IS_SWAPPED, np->dev, np->cname); } else { /* not swap - does it overlap */ rval = meta_check_overlap(snp->cname, np, 0, -1, snp, 0, -1, ep); if (rval != 0) { (void) mdoverlaperror(ep, MDE_OVERLAP_SWAP, np->cname, NULL, snp->cname); } } } free_swapinfo(swtp); /* return success */ return (rval); } /* * Is a driver currently swapped on? */ int meta_check_driveswapped( mdsetname_t *sp, mddrivename_t *dnp, md_error_t *ep ) { struct swaptable *swtp; int nswap; int i; int rval = 0; /* should have a set */ assert(sp != NULL); /* get swap info */ if (get_swapinfo(&swtp, &nswap, ep) != 0) return (-1); /* look for match */ for (i = 0; (i < nswap); ++i) { mdname_t *snp; if ((snp = metaname(&sp, swtp->swt_ent[i].ste_path, LOGICAL_DEVICE, ep)) == NULL) { mdclrerror(ep); continue; } if (strcmp(dnp->cname, snp->drivenamep->cname) == 0) { rval = mddeverror(ep, MDE_IS_SWAPPED, NODEV64, dnp->cname); } } free_swapinfo(swtp); /* return success */ return (rval); } /* * check whether device is a dump device */ static int meta_check_dump( mdsetname_t *sp, mdname_t *np, md_error_t *ep ) { int rval = 0; int dump_fd; char device[MAXPATHLEN]; if ((dump_fd = open("/dev/dump", O_RDONLY)) < 0) return (mdsyserror(ep, errno, "/dev/dump")); if (ioctl(dump_fd, DIOCGETDEV, device) != -1) { mdname_t *dump_np; if ((dump_np = metaname(&sp, device, UNKNOWN, ep)) == NULL) { mdclrerror(ep); (void) close(dump_fd); return (0); } if (np->dev == dump_np->dev) { rval = mddeverror(ep, MDE_IS_DUMP, np->dev, np->cname); } else { /* not a dump device - but does it overlap? */ rval = meta_check_overlap(dump_np->cname, np, 0, -1, dump_np, 0, -1, ep); if (rval != 0) { (void) mdoverlaperror(ep, MDE_OVERLAP_DUMP, np->cname, NULL, dump_np->cname); } } } (void) close(dump_fd); return (rval); } /* * check whether device is mounted */ static int meta_check_mounted( mdsetname_t *sp, mdname_t *np, md_error_t *ep ) { FILE *mfp; struct mnttab m; int rval = 0; char mountp[MNT_LINE_MAX]; char mnt_special[MNT_LINE_MAX]; /* should have a set */ assert(sp != NULL); /* look in mnttab */ if ((mfp = open_mnttab()) == NULL) return (mdsyserror(ep, errno, MNTTAB)); while ((getmntent(mfp, &m) == 0) && (rval == 0)) { char **fstype = skip_these_mntents; int skipit = 0; mdname_t *mnp; if ((m.mnt_special == NULL) || (m.mnt_mountp == NULL)) continue; if (m.mnt_mountp[0] != '/') continue; while (*fstype != NULL) if (strcmp(m.mnt_fstype, *fstype++) == 0) { skipit++; break; } if (skipit == 1) continue; (void) strcpy(mountp, m.mnt_mountp); (void) strcpy(mnt_special, m.mnt_special); if ((mnp = metaname(&sp, mnt_special, UNKNOWN, ep)) == NULL) { mdclrerror(ep); continue; } if (np->dev == mnp->dev) { rval = mduseerror(ep, MDE_IS_MOUNTED, np->dev, mountp, np->cname); } else { /* device isn't in mnttab - does it overlap? */ rval = meta_check_overlap(mnp->cname, np, 0, -1, mnp, 0, -1, ep); if (rval != 0) { (void) mdoverlaperror(ep, MDE_OVERLAP_MOUNTED, np->cname, mountp, mnp->cname); } } } /* return success */ return (rval); } /* * Is a file system currently mounted on this disk drive? */ int meta_check_drivemounted( mdsetname_t *sp, mddrivename_t *dnp, md_error_t *ep ) { FILE *mfp; struct mnttab m; int rval = 0; char mountp[MNT_LINE_MAX]; char mnt_special[MNT_LINE_MAX]; /* should have a set */ assert(sp != NULL); /* look in mnttab */ if ((mfp = open_mnttab()) == NULL) return (mdsyserror(ep, errno, MNTTAB)); while ((getmntent(mfp, &m) == 0) && (rval == 0)) { char **fstype = skip_these_mntents; int skipit = 0; mdname_t *mnp; if ((m.mnt_special == NULL) || (m.mnt_mountp == NULL)) continue; if (m.mnt_mountp[0] != '/') continue; while (*fstype != NULL) if (strcmp(m.mnt_fstype, *fstype++) == 0) { skipit++; break; } if (skipit == 1) continue; (void) strcpy(mountp, m.mnt_mountp); (void) strcpy(mnt_special, m.mnt_special); if ((mnp = metaname(&sp, mnt_special, LOGICAL_DEVICE, ep)) == NULL) { mdclrerror(ep); continue; } if (strcmp(dnp->cname, mnp->drivenamep->cname) == 0) { rval = mduseerror(ep, MDE_IS_MOUNTED, NODEV64, mountp, dnp->cname); } } /* return success */ return (rval); } /* * Check to see if the specified name is already in use or overlaps * with a device already in use. Checks are made to determine whether * the device is mounted, is a swap device, or a dump device. In each * case if the device is not in use then an overlap check is done to ensure * that the specified slice does not overlap. */ int meta_check_inuse( mdsetname_t *sp, mdname_t *np, mdinuseopts_t inuse_flags, md_error_t *ep ) { int rval = 0; if ((inuse_flags & MDCHK_MOUNTED) && (rval = meta_check_mounted(sp, np, ep)) != 0) return (rval); if ((inuse_flags & MDCHK_SWAP) && (rval = meta_check_swapped(sp, np, ep)) != 0) return (rval); if ((inuse_flags & MDCHK_DUMP) && (rval = meta_check_dump(sp, np, ep)) != 0) return (rval); return (rval); } int meta_check_driveinset(mdsetname_t *sp, mddrivename_t *dn, md_error_t *ep) { set_t setno; set_t max_sets; if ((max_sets = get_max_sets(ep)) == 0) return (-1); for (setno = 1; setno < max_sets; setno++) { mdsetname_t *sp1; int is_it; if (setno == sp->setno) continue; if ((sp1 = metasetnosetname(setno, ep)) == NULL) { if (mdismddberror(ep, MDE_DB_NODB)) { mdclrerror(ep); return (0); } if (mdiserror(ep, MDE_NO_SET)) { mdclrerror(ep); continue; } return (-1); } metaflushsetname(sp1); if ((is_it = meta_is_drive_in_thisset(sp1, dn, FALSE, ep)) == -1) return (-1); if (is_it) return (mddserror(ep, MDE_DS_DRIVEINSET, sp->setno, sp1->setname, dn->cname, sp->setname)); } return (0); } /* * Add a device/device id tuple to the devname cache */ static void add_to_devname_list( char *device_name, /* fully qualified dev name */ ddi_devid_t devid /* device id */ ) { dev_list_t *dnlp; dnlp = Zalloc(sizeof (*dnlp)); dnlp->dev_name = Strdup(device_name); dnlp->devid = devid; /* link the node into the devname list */ dnlp->dev_nxt = devnamelist; devnamelist = dnlp; } /* * check for same drive * * Differentiate between matching on name/dev_t and devid. In the latter * case it is correct to fail but misleading to give the same error msg as * for an overlapping slice. * */ int meta_check_samedrive( mdname_t *np1, /* first comp */ mdname_t *np2, /* second comp */ md_error_t *ep ) { mdcinfo_t *cinfop1, *cinfop2; mdnmtype_t type1 = np1->drivenamep->type; mdnmtype_t type2 = np2->drivenamep->type; int l = 0; char *name1 = NULL; char *name2 = NULL; int retval = CANT_TELL; int fd1 = -1; int fd2 = -1; int rc1 = -2, rc2 = -2; uint_t strl1 = 0, strl2 = 0; int devid1_found = 0; int devid2_found = 0; ddi_devid_t devid1 = NULL; ddi_devid_t devid2 = NULL; dev_list_t *dnlp = NULL; assert(type1 != MDT_FAST_META && type1 != MDT_FAST_COMP); assert(type2 != MDT_FAST_META && type2 != MDT_FAST_COMP); /* * The process of determining if 2 names are the same drive is * as follows: * * Case 1 - The filenames are identical * * Case 2 - Both devices have a devid * get and compare the devids for the devices. If both * devices have a devid then the compare will is all * that is needed we are done. * * Case 3 - One or more devices does not have a devid * start by doing a simple compare of the name, if they * are the same just return. * * If the names differ then keep going and see if the * may be the same underlying devic. First check to * see if the sd name is the same (old code). * * Then check the major and minor numbers to see if * they are the same. If they are then return (old code). * * Next compare the raw name and the component name and * if they are the same then return. * * All else has failed so use the component name (cname) * component number and unit number. If they all are * equal then call them the same drive. * */ if ((np1 == NULL) || (np2 == NULL)) return (NOT_SAMEDRIVE); /* if the name structs are the same then the drives must be */ if (np1 == np2) return (IDENTICAL_NAME_DEVT); name1 = np1->bname; name2 = np2->bname; if ((name1 == NULL) || ((strl1 = strlen(name1)) == 0) || (name2 == NULL) || ((strl2 = strlen(name2)) == 0)) return (NOT_SAMEDRIVE); if ((strl1 == strl2) && (strcmp(name1, name2) == 0)) { /* names are identical */ return (IDENTICAL_NAME_DEVT); } if (is_metaname(name1) || is_metaname(name2)) return (NOT_SAMEDRIVE); /* * Check to see if the devicename is in the static list. If so, * use its devid. Otherwise do the expensive operations * of opening the device, getting the devid, and closing the * device. Add the result into the static list. * * The case where this list will be useful is when there are soft * partitions on multiple drives and a new soft partition is being * created. In that situation the underlying physical device name * for the new soft partition would be compared against each of the * existing soft partititions. Without this static list that would * involve 2 opens, closes, and devid gets for each existing soft * partition */ for (dnlp = devnamelist; (dnlp != NULL) && !(devid1_found && devid2_found); dnlp = dnlp->dev_nxt) { if (!devid1_found && (strcmp(dnlp->dev_name, name1) == 0)) { devid1_found = 1; devid1 = dnlp->devid; if (devid1 == NULL) rc1 = 1; else rc1 = 0; continue; } if (!devid2_found && (strcmp(dnlp->dev_name, name2) == 0)) { devid2_found = 1; devid2 = dnlp->devid; if (devid2 == NULL) rc2 = 1; else rc2 = 0; continue; } } /* * Start by checking if the device has a device id, and if they * are equal. If they are there is no question there is a match. * * The process here is open each disk, get the devid for each * disk. If they both have a devid compare them and return * the results. */ if (!devid1_found) { if ((fd1 = open(name1, O_RDONLY | O_NDELAY)) < 0) { return (NOT_SAMEDRIVE); } rc1 = devid_get(fd1, &devid1); (void) close(fd1); /* add the name and devid to the cache */ add_to_devname_list(name1, devid1); } if (!devid2_found) { if ((fd2 = open(name2, O_RDONLY | O_NDELAY)) < 0) { return (NOT_SAMEDRIVE); } rc2 = devid_get(fd2, &devid2); (void) close(fd2); /* add the name and devid to the cache */ add_to_devname_list(name2, devid2); } if ((rc1 == 0) && (rc2 == 0)) { if (devid_compare(devid1, devid2) == 0) retval = IDENTICAL_DEVIDS; /* same devid */ else retval = NOT_SAMEDRIVE; /* different drives */ } if (retval >= 0) { return (retval); } /* * At this point in time one of the two drives did not have a * device ID. Do not make the assumption that is one drive * did have a device id and the other did not that they are not * the same. One drive could be covered by a device and still * be the same drive. This is a general flaw in the system at * this time. */ /* * The optimization can not happen if we are given an old style name * in the form /dev/XXNN[a-h], since the name caches differently and * allows overlaps to happen. */ if (! ((sscanf(np1->bname, "/dev/%*[^0-9/]%*u%*[a-h]%n", &l) == 0 && l == strlen(np1->bname)) || (sscanf(np2->bname, "/dev/%*[^0-9/]%*u%*[a-h]%n", &l) == 0 && l == strlen(np2->bname))) && ((type1 == MDT_COMP) || (type1 == MDT_META)) && ((type2 == MDT_COMP) || (type2 == MDT_META))) if (np1->drivenamep == np2->drivenamep) return (IDENTICAL_NAME_DEVT); else return (NOT_SAMEDRIVE); /* check for same drive */ if (meta_getmajor(np1->dev) != meta_getmajor(np2->dev)) return (NOT_SAMEDRIVE); /* not same drive */ if (((cinfop1 = metagetcinfo(np1, ep)) == NULL) || ((cinfop2 = metagetcinfo(np2, ep)) == NULL)) { if ((strcmp(np1->drivenamep->cname, np2->drivenamep->cname) != 0) && (strcmp(np1->drivenamep->rname, np2->drivenamep->rname) != 0)) { mdclrerror(ep); return (NOT_SAMEDRIVE); /* not same drive */ } else { return (CANT_TELL); /* can't tell */ } } else if ((strncmp(cinfop1->cname, cinfop2->cname, sizeof (cinfop1->cname)) != 0) || (cinfop1->cnum != cinfop2->cnum) || (cinfop1->unit != cinfop2->unit)) { return (NOT_SAMEDRIVE); /* not same drive */ } /* same drive */ return (IDENTICAL_NAME_DEVT); } /* * check for overlap */ int meta_check_overlap( char *uname, /* user supplied name for errors */ mdname_t *np1, /* first comp */ diskaddr_t slblk1, /* first comp - start logical block */ diskaddr_t nblks1, /* first comp - # of blocks */ mdname_t *np2, /* second comp */ diskaddr_t slblk2, /* second comp - start logical block */ diskaddr_t nblks2, /* second comp - # of blocks */ md_error_t *ep ) { diskaddr_t sblk1, sblk2; mdvtoc_t *vtocp1, *vtocp2; uint_t partno1, partno2; mdpart_t *partp1, *partp2; int ret; /* verify args */ if (slblk1 == MD_DISKADDR_ERROR) { assert(0); return (mdsyserror(ep, EINVAL, np1->cname)); } if (slblk2 == MD_DISKADDR_ERROR) { assert(0); return (mdsyserror(ep, EINVAL, np2->cname)); } /* check for same drive */ if ((ret = meta_check_samedrive(np1, np2, ep)) == 0) { return (0); /* not same drive */ } else if (ret < 0) { return (-1); /* can't tell */ } /* check for overlap */ if (((vtocp1 = metagetvtoc(np1, FALSE, &partno1, ep)) == NULL) || ((vtocp2 = metagetvtoc(np2, FALSE, &partno2, ep)) == NULL)) { return (-1); /* can't tell */ } partp1 = &vtocp1->parts[partno1]; partp2 = &vtocp2->parts[partno2]; sblk1 = partp1->start + slblk1; if (nblks1 == -1) nblks1 = partp1->size - slblk1; sblk2 = partp2->start + slblk2; if (nblks2 == -1) nblks2 = partp2->size - slblk2; if (((sblk1 >= sblk2) && (sblk1 < (sblk2 + nblks2))) || ((sblk2 >= sblk1) && (sblk2 < (sblk1 + nblks1)))) { if (np1->dev == np2->dev) { /* slice in use */ return (mduseerror(ep, MDE_ALREADY, np1->dev, uname, np1->cname)); } if (ret == IDENTICAL_NAME_DEVT) return (mduseerror(ep, /* slice overlaps */ MDE_OVERLAP, np1->dev, uname, np1->cname)); else return (mduseerror(ep, /* same devid */ MDE_SAME_DEVID, np1->dev, uname, np2->cname)); } /* return success */ return (0); /* no overlap */ } /* * check to see if a device is in a metadevice */ int meta_check_inmeta( mdsetname_t *sp, mdname_t *np, mdchkopts_t options, diskaddr_t slblk, diskaddr_t nblks, md_error_t *ep ) { uint_t partno; /* see if replica slice is ok, only applies to disks in sets */ if (! (options & MDCHK_ALLOW_REPSLICE) && ! metaislocalset(sp)) { uint_t rep_slice; if (metagetvtoc(np, FALSE, &partno, ep) == NULL) return (-1); if (meta_replicaslice(np->drivenamep, &rep_slice, ep) != 0) return (-1); if (partno == rep_slice) return (mddeverror(ep, MDE_REPCOMP_INVAL, np->dev, np->cname)); } /* check for databases */ if (meta_check_inreplica(sp, np, slblk, nblks, ep) != 0) { if (mdisuseerror(ep, MDE_ALREADY)) { if (options & MDCHK_ALLOW_MDDB) { mdclrerror(ep); } else { return (mddeverror(ep, MDE_HAS_MDDB, np->dev, np->cname)); } } else { return (-1); } } /* check metadevices */ if (meta_check_instripe(sp, np, slblk, nblks, ep) != 0) return (-1); if (meta_check_inmirror(sp, np, slblk, nblks, ep) != 0) return (-1); if (meta_check_intrans(sp, np, options, slblk, nblks, ep) != 0) return (-1); if (meta_check_insp(sp, np, slblk, nblks, ep) != 0) return (-1); if (! (options & MDCHK_ALLOW_HS)) { if (meta_check_inhsp(sp, np, slblk, nblks, ep) != 0) return (-1); } if (meta_check_inraid(sp, np, slblk, nblks, ep) != 0) return (-1); /* return success */ return (0); } /* * check to see if a device is in its set */ int meta_check_inset( mdsetname_t *sp, mdname_t *np, md_error_t *ep ) { mdsetname_t *npsp; int bypass_daemon = FALSE; /* check devices set */ if (metaislocalset(sp)) bypass_daemon = TRUE; if ((npsp = metagetset(np, bypass_daemon, ep)) == NULL) { if ((! metaismeta(np)) && (metaislocalset(sp)) && (mdismddberror(ep, MDE_DB_NODB))) { mdclrerror(ep); npsp = sp; } else { return (-1); } } /* check set */ if (metaissameset(sp, npsp)) return (0); /* return appropriate error */ if (metaislocalset(sp)) return (mddeverror(ep, MDE_IN_SHARED_SET, np->dev, np->cname)); else return (mddeverror(ep, MDE_NOT_IN_SET, np->dev, np->cname)); } /* * check to see if current user is root */ int meta_check_root(md_error_t *ep) { if (geteuid() != 0) { (void) mderror(ep, MDE_NOPERM, ""); return (-1); } return (0); }