Beispiel #1
0
/*
 * 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);
}
Beispiel #2
0
/*
 * 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);
}
Beispiel #3
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.
 */
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);
}
Beispiel #4
0
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);
}
Beispiel #5
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);
}
Beispiel #6
0
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);
	}
}
Beispiel #8
0
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);
}