/* * 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); }
/* * Convert from a devid string to a path. */ static char * devid_to_path(char *devid_str) { ddi_devid_t devid; char *minor; char *path; devid_nmlist_t *list = NULL; int ret; if (devid_str_decode(devid_str, &devid, &minor) != 0) return (NULL); ret = devid_deviceid_to_nmlist("/dev", devid, minor, &list); devid_str_free(minor); devid_free(devid); if (ret != 0) return (NULL); if ((path = strdup(list[0].devname)) == NULL) return (NULL); devid_free_nmlist(list); return (path); }
/* * Check if we have the drive in our list, based upon the device id. * We got the device id from the dev tree walk. This is encoded * using devid_str_encode(3DEVID). In order to check the device ids we need * to use the devid_compare(3DEVID) function, so we need to decode the * string representation of the device id. */ descriptor_t * drive_get_descriptor_by_name(char *name, int *errp) { ddi_devid_t devid; descriptor_t **drives; descriptor_t *drive = NULL; int i; if (name == NULL || devid_str_decode(name, &devid, NULL) != 0) { *errp = EINVAL; return (NULL); } drives = cache_get_descriptors(DM_DRIVE, errp); if (*errp != 0) { devid_free(devid); return (NULL); } /* * We have to loop through all of them, freeing the ones we don't * want. Once drive is set, we don't need to compare any more. */ for (i = 0; drives[i]; i++) { if (drive == NULL && drives[i]->p.disk->devid != NULL && devid_compare(devid, drives[i]->p.disk->devid) == 0) { drive = drives[i]; } else { /* clean up the unused descriptor */ cache_free_descriptor(drives[i]); } } free(drives); devid_free(devid); if (drive == NULL) { *errp = ENODEV; } return (drive); }
int scsi_detach_lun(struct scsibus_softc *sc, int target, int lun, int flags) { struct scsi_link *alink = sc->adapter_link; struct scsi_link *link; int rv; if (target < 0 || target >= alink->adapter_buswidth || target == alink->adapter_target || lun < 0 || lun >= alink->luns) return (ENXIO); link = scsi_get_link(sc, target, lun); if (link == NULL) return (ENXIO); if (((flags & DETACH_FORCE) == 0) && (link->flags & SDEV_OPEN)) return (EBUSY); /* detaching a device from scsibus is a five step process... */ /* 1. wake up processes sleeping for an xs */ scsi_link_shutdown(link); /* 2. detach the device */ rv = config_detach(link->device_softc, flags); if (rv != 0) return (rv); /* 3. if its using the openings io allocator, clean it up */ if (ISSET(link->flags, SDEV_OWN_IOPL)) { scsi_iopool_destroy(link->pool); free(link->pool, M_DEVBUF); } /* 4. free up its state in the adapter */ if (alink->adapter->dev_free != NULL) alink->adapter->dev_free(link); /* 5. free up its state in the midlayer */ if (link->id != NULL) devid_free(link->id); scsi_remove_link(sc, link); free(link, M_DEVBUF); return (0); }
/* * Check if we have the drive in our list, based upon the device id. * We got the device id from the dev tree walk. This is encoded * using devid_str_encode(3DEVID). In order to check the device ids we need * to use the devid_compare(3DEVID) function, so we need to decode the * string representation of the device id. */ static disk_t * get_disk_by_deviceid(disk_t *listp, char *devidstr) { ddi_devid_t devid; if (devidstr == NULL || devid_str_decode(devidstr, &devid, NULL) != 0) { return (NULL); } while (listp != NULL) { if (listp->devid != NULL && devid_compare(listp->devid, devid) == 0) { break; } listp = listp->next; } devid_free(devid); return (listp); }
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); }
/* * It's up to the caller to do any sorting or pretty-printing of the device * mappings we report. Since we're storing the device links as just the cXtYdZ * part, we'll add /dev/rdsk/ back on when we print the listing so we maintain * compatibility with previous versions of this tool. There's a little bit * of footwork involved to make sure that we show all the paths to a device * rather than just the first one we stashed away. */ static void list_devs(int listguids, int ctrl) { nvlist_t *thisdevnvl; nvpair_t *pair; char *diskpath, *livepath, *key, *querydev; char *matchctrl = NULL; char checkctrl[MAXPATHLEN]; int rv; if (!mpxenabled) { if (mpxprop) { logmsg(MSG_ERROR, gettext("MPXIO disabled\n")); } else { logmsg(MSG_ERROR, gettext("No STMS devices have " "been found\n")); } return; } if (listguids) { (void) printf(gettext("non-STMS device name\t\t\tGUID\n" "------------------------------------------" "------------------------\n")); } else { (void) printf(gettext("non-STMS device name\t\t\t" "STMS device name\n" "------------------------------------------" "------------------------\n")); } bzero(checkctrl, MAXPATHLEN); pair = NULL; while ((pair = nvlist_next_nvpair(mapnvl, pair)) != NULL) { boolean_t livescsivhcip = B_FALSE; if ((((rv = nvpair_value_string(pair, &querydev)) < 0) || ((key = nvpair_name(pair)) == NULL)) || ((strstr(key, "/pci") != NULL) || (strstr(key, "/sbus") != NULL) || (strstr(key, "/scsi_vhci") != NULL) || (strncmp(key, "id1", 3) == 0))) { logmsg(MSG_INFO, "list_devs: rv = %d; (%s) is not a devlink, " "continuing.\n", rv, (key != NULL) ? key : "null"); querydev = NULL; continue; } (void) nvlist_lookup_nvlist(mapnvl, querydev, &thisdevnvl); (void) nvlist_lookup_boolean_value(thisdevnvl, NVL_MPXEN, &livescsivhcip); (void) nvlist_lookup_string(thisdevnvl, NVL_MPXPATH, &livepath); if ((!livescsivhcip) || (livescsivhcip && (strncmp(key, livepath, strlen(key)) == 0))) continue; (void) nvlist_lookup_string(thisdevnvl, NVL_PATH, &diskpath); logmsg(MSG_INFO, "list_devs: %s :: %s ::%s :: MPXEN (%s)\n", key, diskpath, livepath, ((livescsivhcip) ? "TRUE" : "FALSE")); if (ctrl > -1) { (void) sprintf(checkctrl, "c%dt", ctrl); matchctrl = strstr(key, checkctrl); if (matchctrl == NULL) continue; } if (listguids != 0) { char *tempguid; ddi_devid_t curdevid; int rv; rv = devid_str_decode(querydev, &curdevid, NULL); if (rv == -1) { logmsg(MSG_INFO, "Unable to decode devid %s\n", key); continue; } tempguid = devid_to_guid(curdevid); if (tempguid != NULL) (void) printf("/dev/rdsk/%s\t%s\n", diskpath, tempguid); devid_free_guid(tempguid); devid_free(curdevid); continue; } (void) printf("/dev/rdsk/%s\t/dev/rdsk/%s\n", (strstr(key, diskpath) == NULL) ? key : diskpath, livepath); } }
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); }