Beispiel #1
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 #2
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 #3
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);
}
Beispiel #4
0
/*
 * Go through and fix up any path and/or devid information for the given vdev
 * configuration.
 */
static int
fix_paths(nvlist_t *nv, name_entry_t *names)
{
	nvlist_t **child;
	uint_t c, children;
	uint64_t guid;
	name_entry_t *ne, *best;
	char *path, *devid;

	if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
	    &child, &children) == 0) {
		for (c = 0; c < children; c++)
			if (fix_paths(child[c], names) != 0)
				return (-1);
		return (0);
	}

	/*
	 * This is a leaf (file or disk) vdev.  In either case, go through
	 * the name list and see if we find a matching guid.  If so, replace
	 * the path and see if we can calculate a new devid.
	 *
	 * There may be multiple names associated with a particular guid, in
	 * which case we have overlapping partitions or multiple paths to the
	 * same disk.  In this case we prefer to use the path name which
	 * matches the ZPOOL_CONFIG_PATH.  If no matching entry is found we
	 * use the lowest order device which corresponds to the first match
	 * while traversing the ZPOOL_IMPORT_PATH search path.
	 */
	verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0);
	if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0)
		path = NULL;

	best = NULL;
	for (ne = names; ne != NULL; ne = ne->ne_next) {
		if (ne->ne_guid == guid) {

			if (path == NULL) {
				best = ne;
				break;
			}

			if ((strlen(path) == strlen(ne->ne_name)) &&
			    strncmp(path, ne->ne_name, strlen(path)) == 0) {
				best = ne;
				break;
			}

			if (best == NULL) {
				best = ne;
				continue;
			}

			/* Prefer paths with move vdev labels. */
			if (ne->ne_num_labels > best->ne_num_labels) {
				best = ne;
				continue;
			}

			/* Prefer paths earlier in the search order. */
			if (ne->ne_num_labels == best->ne_num_labels &&
			    ne->ne_order < best->ne_order) {
				best = ne;
				continue;
			}
		}
	}

	if (best == NULL)
		return (0);

	if (nvlist_add_string(nv, ZPOOL_CONFIG_PATH, best->ne_name) != 0)
		return (-1);

	if ((devid = get_devid(best->ne_name)) == NULL) {
		(void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID);
	} else {
		if (nvlist_add_string(nv, ZPOOL_CONFIG_DEVID, devid) != 0) {
			devid_str_free(devid);
			return (-1);
		}
		devid_str_free(devid);
	}

	return (0);
}
Beispiel #5
0
/*
 * Given a vdev, return the name to display in iostat.  If the vdev has a path,
 * we use that, stripping off any leading "/dev/dsk/"; if not, we use the type.
 * We also check if this is a whole disk, in which case we strip off the
 * trailing 's0' slice name.
 *
 * This routine is also responsible for identifying when disks have been
 * reconfigured in a new location.  The kernel will have opened the device by
 * devid, but the path will still refer to the old location.  To catch this, we
 * first do a path -> devid translation (which is fast for the common case).  If
 * the devid matches, we're done.  If not, we do a reverse devid -> path
 * translation and issue the appropriate ioctl() to update the path of the vdev.
 * If 'zhp' is NULL, then this is an exported pool, and we don't need to do any
 * of these checks.
 */
char *
zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv)
{
	char *path, *devid;
	uint64_t value;
	char buf[64];

	if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT,
	    &value) == 0) {
		verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID,
		    &value) == 0);
		(void) snprintf(buf, sizeof (buf), "%llu", value);
		path = buf;
	} else if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0) {

		if (zhp != NULL &&
		    nvlist_lookup_string(nv, ZPOOL_CONFIG_DEVID, &devid) == 0) {
			/*
			 * Determine if the current path is correct.
			 */
			char *newdevid = path_to_devid(path);

			if (newdevid == NULL ||
			    strcmp(devid, newdevid) != 0) {
				char *newpath;

				if ((newpath = devid_to_path(devid)) != NULL) {
					/*
					 * Update the path appropriately.
					 */
					set_path(zhp, nv, newpath);
					if (nvlist_add_string(nv,
					    ZPOOL_CONFIG_PATH, newpath) == 0)
						verify(nvlist_lookup_string(nv,
						    ZPOOL_CONFIG_PATH,
						    &path) == 0);
					free(newpath);
				}
			}

			if (newdevid)
				devid_str_free(newdevid);
		}

		if (strncmp(path, "/dev/dsk/", 9) == 0)
			path += 9;

		if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
		    &value) == 0 && value) {
			char *tmp = zfs_strdup(hdl, path);
			if (tmp == NULL)
				return (NULL);
			tmp[strlen(path) - 2] = '\0';
			return (tmp);
		}
	} else {
		verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &path) == 0);

		/*
		 * If it's a raidz device, we need to stick in the parity level.
		 */
		if (strcmp(path, VDEV_TYPE_RAIDZ) == 0) {
			verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NPARITY,
			    &value) == 0);
			(void) snprintf(buf, sizeof (buf), "%s%llu", path,
			    value);
			path = buf;
		}
	}

	return (zfs_strdup(hdl, path));
}
Beispiel #6
0
/*
 * Go through and fix up any path and/or devid information for the given vdev
 * configuration.
 */
static int
fix_paths(nvlist_t *nv, name_entry_t *names)
{
	nvlist_t **child;
	uint_t c, children;
	uint64_t guid;
	name_entry_t *ne, *best;
	char *path, *devid;
	int matched;

	if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
	    &child, &children) == 0) {
		for (c = 0; c < children; c++)
			if (fix_paths(child[c], names) != 0)
				return (-1);
		return (0);
	}

	/*
	 * This is a leaf (file or disk) vdev.  In either case, go through
	 * the name list and see if we find a matching guid.  If so, replace
	 * the path and see if we can calculate a new devid.
	 *
	 * There may be multiple names associated with a particular guid, in
	 * which case we have overlapping slices or multiple paths to the same
	 * disk.  If this is the case, then we want to pick the path that is
	 * the most similar to the original, where "most similar" is the number
	 * of matching characters starting from the end of the path.  This will
	 * preserve slice numbers even if the disks have been reorganized, and
	 * will also catch preferred disk names if multiple paths exist.
	 */
	verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0);
	if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0)
		path = NULL;

	matched = 0;
	best = NULL;
	for (ne = names; ne != NULL; ne = ne->ne_next) {
		if (ne->ne_guid == guid) {
			const char *src, *dst;
			int count;

			if (path == NULL) {
				best = ne;
				break;
			}

			src = ne->ne_name + strlen(ne->ne_name) - 1;
			dst = path + strlen(path) - 1;
			for (count = 0; src >= ne->ne_name && dst >= path;
			    src--, dst--, count++)
				if (*src != *dst)
					break;

			/*
			 * At this point, 'count' is the number of characters
			 * matched from the end.
			 */
			if (count > matched || best == NULL) {
				best = ne;
				matched = count;
			}
		}
	}

	if (best == NULL)
		return (0);

	if (nvlist_add_string(nv, ZPOOL_CONFIG_PATH, best->ne_name) != 0)
		return (-1);

	if ((devid = get_devid(best->ne_name)) == NULL) {
		(void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID);
	} else {
		if (nvlist_add_string(nv, ZPOOL_CONFIG_DEVID, devid) != 0) {
			devid_str_free(devid);
			return (-1);
		}
		devid_str_free(devid);
	}

	return (0);
}
static int
mpxio_nvl_boilerplate(di_node_t curnode)
{
	int		rv;
	char		*strdevid;
	ddi_devid_t	curdevid;
	nvlist_t	*newnvl;

	for (; curnode != DI_NODE_NIL; curnode = di_drv_next_node(curnode)) {
		errno = 0;

		curdevid = NULL;
		get_devid(curnode, &curdevid);
		if (curdevid == NULL)
			/*
			 * There's no devid registered for this device
			 * so it's not cool enough to play with us
			 */
			continue;

		strdevid = devid_str_encode(curdevid, NULL);
		/* does this exist in the on-disk cache? */
		rv = nvlist_lookup_nvlist(mapnvl, strdevid, &newnvl);
		if (rv == ENOENT) {
			logmsg(MSG_INFO, "nvlist for %s not found\n", strdevid);
			/* no, so alloc a new nvl to store it */
			if (nvlist_alloc(&newnvl, NV_UNIQUE_NAME, 0) != 0) {
				logmsg(MSG_ERROR,
				    gettext("Unable to allocate space for "
				    "a devid property list: %s\n"),
				    strerror(errno));
				return (-1);
			}
		} else {
			if ((rv != ENOTSUP) && (rv != EINVAL))
				logmsg(MSG_INFO,
				    "%s exists in ondisknvl, verifying\n",
				    strdevid);
		}

		if (popcheck_devnvl(curnode, newnvl, strdevid) != 0) {
			logmsg(MSG_ERROR,
			    gettext("Unable to populate devid nvpair "
			    "for device with devid %s\n"),
			    strdevid);
			devid_str_free(strdevid);
			nvlist_free(newnvl);
			return (-1);
		}

		/* Now add newnvl into our cache. */
		errno = 0;
		rv = nvlist_add_nvlist(mapnvl, strdevid, newnvl);
		if (rv) {
			logmsg(MSG_ERROR,
			    gettext("Unable to add device (devid %s) "
			    "to in-kernel nvl: %s (%d)\n"),
			    strdevid, strerror(rv), rv);
			devid_str_free(strdevid);
			nvlist_free(newnvl);
			return (-1);
		}
		logmsg(MSG_INFO,
		    gettext("added device (devid %s) to mapnvl\n\n"),
		    strdevid);
		devid_str_free(strdevid);
	}
	return (0);
}
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);
}