/* * handle case where device has been probed but its target driver is not * attached so enumeration has not quite finished. Opening the /devices * pathname will force the kernel to finish the enumeration process and * let us get the data we need. */ static void get_devid(di_node_t node, ddi_devid_t *thisdevid) { int fd; char realpath[MAXPATHLEN]; char *openpath = di_devfs_path(node); errno = 0; bzero(realpath, MAXPATHLEN); if (strstr(openpath, "/devices") == NULL) { (void) snprintf(realpath, MAXPATHLEN, "/devices%s:c,raw", openpath); fd = open(realpath, O_RDONLY|O_NDELAY); } else { fd = open(openpath, O_RDONLY|O_NDELAY); } if (fd < 0) { logmsg(MSG_INFO, "Unable to open path %s: %s\n", openpath, strerror(errno)); return; } if (devid_get(fd, thisdevid) != 0) { logmsg(MSG_INFO, "'%s' node (%s) without a devid registered\n", di_driver_name(node), di_devfs_path(node)); } (void) close(fd); }
/* * Given a /devices path, lookup the corresponding devid for each minor node, * and find any vdevs with matching devids. Doing this straight up would be * rather inefficient, O(minor nodes * vdevs in system), so we take advantage of * the fact that each devid ends with "/<minornode>". Once we find any valid * minor node, we chop off the portion after the last slash, and then search for * matching vdevs, which is O(vdevs in system). */ static boolean_t devid_iter(const char *devpath, zfs_process_func_t func, boolean_t wholedisk) { size_t len = strlen(devpath) + sizeof ("/devices") + sizeof (PHYS_PATH) - 1; char *fullpath; int fd; ddi_devid_t devid; char *devidstr, *fulldevid; dev_data_t data = { 0 }; /* * Try to open a known minor node. */ fullpath = alloca(len); (void) snprintf(fullpath, len, "/devices%s%s", devpath, PHYS_PATH); if ((fd = open(fullpath, O_RDONLY)) < 0) return (B_FALSE); /* * Determine the devid as a string, with no trailing slash for the minor * node. */ if (devid_get(fd, &devid) != 0) { (void) close(fd); return (B_FALSE); } (void) close(fd); if ((devidstr = devid_str_encode(devid, NULL)) == NULL) { devid_free(devid); return (B_FALSE); } len = strlen(devidstr) + 2; fulldevid = alloca(len); (void) snprintf(fulldevid, len, "%s/", devidstr); data.dd_compare = fulldevid; data.dd_func = func; data.dd_prop = ZPOOL_CONFIG_DEVID; data.dd_found = B_FALSE; data.dd_isdisk = wholedisk; (void) zpool_iter(g_zfshdl, zfs_iter_pool, &data); devid_str_free(devidstr); devid_free(devid); return (data.dd_found); }
static char * get_devid(const char *path) { int fd; ddi_devid_t devid; char *minor, *ret; if ((fd = open(path, O_RDONLY)) < 0) return (NULL); minor = NULL; ret = NULL; if (devid_get(fd, &devid) == 0) { if (devid_get_minor_name(fd, &minor) == 0) ret = devid_str_encode(devid, minor); if (minor != NULL) devid_str_free(minor); devid_free(devid); } (void) close(fd); return (ret); }
/* * 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); }
static int get_attrs(descriptor_t *dp, int fd, nvlist_t *attrs) { struct dk_minfo minfo; int status; int data_format = FMT_UNKNOWN; int snum = -1; int error; struct extvtoc vtoc; struct dk_gpt *efip; struct dk_cinfo dkinfo; int cooked_fd; struct stat buf; if (fd < 0) { return (ENODEV); } /* First make sure media is inserted and spun up. */ if (!media_read_info(fd, &minfo)) { return (ENODEV); } if ((status = read_extvtoc(fd, &vtoc)) >= 0) { data_format = FMT_VTOC; } else if (status == VT_ENOTSUP && efi_alloc_and_read(fd, &efip) >= 0) { data_format = FMT_EFI; if (nvlist_add_boolean(attrs, DM_EFI) != 0) { efi_free(efip); return (ENOMEM); } } if (data_format == FMT_UNKNOWN) { return (ENODEV); } if (ioctl(fd, DKIOCINFO, &dkinfo) >= 0) { snum = dkinfo.dki_partition; } /* check the slice */ if (data_format == FMT_VTOC) { if (snum < 0 || snum >= vtoc.v_nparts || vtoc.v_part[snum].p_size == 0) { return (ENODEV); } } else { /* data_format == FMT_EFI */ if (snum < 0 || snum >= efip->efi_nparts || efip->efi_parts[snum].p_size == 0) { efi_free(efip); return (ENODEV); } } /* the slice exists */ if (nvlist_add_uint32(attrs, DM_INDEX, snum) != 0) { if (data_format == FMT_EFI) { efi_free(efip); } return (ENOMEM); } if (data_format == FMT_VTOC) { if (nvlist_add_uint64(attrs, DM_START, vtoc.v_part[snum].p_start) != 0) { return (ENOMEM); } if (nvlist_add_uint64(attrs, DM_SIZE, vtoc.v_part[snum].p_size) != 0) { return (ENOMEM); } if (nvlist_add_uint32(attrs, DM_TAG, vtoc.v_part[snum].p_tag) != 0) { return (ENOMEM); } if (nvlist_add_uint32(attrs, DM_FLAG, vtoc.v_part[snum].p_flag) != 0) { return (ENOMEM); } } else { /* data_format == FMT_EFI */ if (nvlist_add_uint64(attrs, DM_START, efip->efi_parts[snum].p_start) != 0) { efi_free(efip); return (ENOMEM); } if (nvlist_add_uint64(attrs, DM_SIZE, efip->efi_parts[snum].p_size) != 0) { efi_free(efip); return (ENOMEM); } if (efip->efi_parts[snum].p_name[0] != 0) { char label[EFI_PART_NAME_LEN + 1]; (void) snprintf(label, sizeof (label), "%.*s", EFI_PART_NAME_LEN, efip->efi_parts[snum].p_name); if (nvlist_add_string(attrs, DM_EFI_NAME, label) != 0) { efi_free(efip); return (ENOMEM); } } } if (data_format == FMT_EFI) { efi_free(efip); } if (inuse_mnt(dp->name, attrs, &error)) { if (error != 0) return (error); } if (fstat(fd, &buf) != -1) { if (nvlist_add_uint64(attrs, DM_DEVT, buf.st_rdev) != 0) { return (ENOMEM); } } /* * We need to open the cooked slice (not the raw one) to get the * correct devid. */ cooked_fd = open(dp->name, O_RDONLY|O_NDELAY); if (cooked_fd >= 0) { int no_mem = 0; ddi_devid_t devid; if (devid_get(cooked_fd, &devid) == 0) { char *minor; if (devid_get_minor_name(cooked_fd, &minor) == 0) { char *devidstr; if ((devidstr = devid_str_encode(devid, minor)) != 0) { if (nvlist_add_string(attrs, DM_DEVICEID, devidstr) != 0) { no_mem = 1; } devid_str_free(devidstr); } devid_str_free(minor); } devid_free(devid); } (void) close(cooked_fd); if (no_mem) { return (ENOMEM); } } return (0); }
/* * Create a leaf vdev. Determine if this is a file or a device. If it's a * device, fill in the device id to make a complete nvlist. Valid forms for a * leaf vdev are: * * /dev/dsk/xxx Complete disk path * /xxx Full path to file * xxx Shorthand for /dev/dsk/xxx */ nvlist_t * make_leaf_vdev(const char *arg) { char path[MAXPATHLEN]; struct stat statbuf; nvlist_t *vdev = NULL; char *type = NULL; boolean_t wholedisk = B_FALSE; /* * Determine what type of vdev this is, and put the full path into * 'path'. We detect whether this is a device of file afterwards by * checking the st_mode of the file. */ if (arg[0] == '/') { /* * Complete device or file path. Exact type is determined by * examining the file descriptor afterwards. */ if (is_whole_disk(arg, &statbuf)) { wholedisk = B_TRUE; } else if (stat(arg, &statbuf) != 0) { (void) fprintf(stderr, gettext("cannot open '%s': %s\n"), arg, strerror(errno)); return (NULL); } (void) strlcpy(path, arg, sizeof (path)); } else { /* * This may be a short path for a device, or it could be total * gibberish. Check to see if it's a known device in * /dev/dsk/. As part of this check, see if we've been given a * an entire disk (minus the slice number). */ (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, arg); if (is_whole_disk(path, &statbuf)) { wholedisk = B_TRUE; } else if (stat(path, &statbuf) != 0) { /* * If we got ENOENT, then the user gave us * gibberish, so try to direct them with a * reasonable error message. Otherwise, * regurgitate strerror() since it's the best we * can do. */ if (errno == ENOENT) { (void) fprintf(stderr, gettext("cannot open '%s': no such " "device in %s\n"), arg, DISK_ROOT); (void) fprintf(stderr, gettext("must be a full path or " "shorthand device name\n")); return (NULL); } else { (void) fprintf(stderr, gettext("cannot open '%s': %s\n"), path, strerror(errno)); return (NULL); } } } /* * Determine whether this is a device or a file. */ if (S_ISBLK(statbuf.st_mode)) { type = VDEV_TYPE_DISK; } else if (S_ISREG(statbuf.st_mode)) { type = VDEV_TYPE_FILE; } else { (void) fprintf(stderr, gettext("cannot use '%s': must be a " "block device or regular file\n"), path); return (NULL); } /* * Finally, we have the complete device or file, and we know that it is * acceptable to use. Construct the nvlist to describe this vdev. All * vdevs have a 'path' element, and devices also have a 'devid' element. */ verify(nvlist_alloc(&vdev, NV_UNIQUE_NAME, 0) == 0); verify(nvlist_add_string(vdev, ZPOOL_CONFIG_PATH, path) == 0); verify(nvlist_add_string(vdev, ZPOOL_CONFIG_TYPE, type) == 0); if (strcmp(type, VDEV_TYPE_DISK) == 0) verify(nvlist_add_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK, (uint64_t)wholedisk) == 0); /* * For a whole disk, defer getting its devid until after labeling it. */ if (S_ISBLK(statbuf.st_mode) && !wholedisk) { /* * Get the devid for the device. */ int fd; ddi_devid_t devid; char *minor = NULL, *devid_str = NULL; if ((fd = open(path, O_RDONLY)) < 0) { (void) fprintf(stderr, gettext("cannot open '%s': " "%s\n"), path, strerror(errno)); nvlist_free(vdev); return (NULL); } if (devid_get(fd, &devid) == 0) { if (devid_get_minor_name(fd, &minor) == 0 && (devid_str = devid_str_encode(devid, minor)) != NULL) { verify(nvlist_add_string(vdev, ZPOOL_CONFIG_DEVID, devid_str) == 0); } if (devid_str != NULL) devid_str_free(devid_str); if (minor != NULL) devid_str_free(minor); devid_free(devid); } (void) close(fd); } return (vdev); }