diff options
Diffstat (limited to 'cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c')
-rw-r--r-- | cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c | 515 |
1 files changed, 431 insertions, 84 deletions
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c index 1c77045..166c831 100644 --- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c +++ b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -213,11 +213,13 @@ add_config(libzfs_handle_t *hdl, pool_list_t *pl, const char *path, name_entry_t *ne; /* - * If this is a hot spare not currently in use, add it to the list of - * names to translate, but don't do anything else. + * If this is a hot spare not currently in use or level 2 cache + * device, add it to the list of names to translate, but don't do + * anything else. */ if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE, - &state) == 0 && state == POOL_STATE_SPARE && + &state) == 0 && + (state == POOL_STATE_SPARE || state == POOL_STATE_L2CACHE) && nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, &vdev_guid) == 0) { if ((ne = zfs_alloc(hdl, sizeof (name_entry_t))) == NULL) return (-1); @@ -361,6 +363,46 @@ pool_active(libzfs_handle_t *hdl, const char *name, uint64_t guid, return (0); } +static nvlist_t * +refresh_config(libzfs_handle_t *hdl, nvlist_t *config) +{ + nvlist_t *nvl; + zfs_cmd_t zc = { 0 }; + int err; + + if (zcmd_write_conf_nvlist(hdl, &zc, config) != 0) + return (NULL); + + if (zcmd_alloc_dst_nvlist(hdl, &zc, + zc.zc_nvlist_conf_size * 2) != 0) { + zcmd_free_nvlists(&zc); + return (NULL); + } + + while ((err = ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_TRYIMPORT, + &zc)) != 0 && errno == ENOMEM) { + if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { + zcmd_free_nvlists(&zc); + return (NULL); + } + } + + if (err) { + (void) zpool_standard_error(hdl, errno, + dgettext(TEXT_DOMAIN, "cannot discover pools")); + zcmd_free_nvlists(&zc); + return (NULL); + } + + if (zcmd_read_dst_nvlist(hdl, &zc, &nvl) != 0) { + zcmd_free_nvlists(&zc); + return (NULL); + } + + zcmd_free_nvlists(&zc); + return (nvl); +} + /* * Convert our list of pools into the definitive set of configurations. We * start by picking the best config for each toplevel vdev. Once that's done, @@ -369,26 +411,25 @@ pool_active(libzfs_handle_t *hdl, const char *name, uint64_t guid, * return to the user. */ static nvlist_t * -get_configs(libzfs_handle_t *hdl, pool_list_t *pl) +get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok) { pool_entry_t *pe; vdev_entry_t *ve; config_entry_t *ce; nvlist_t *ret = NULL, *config = NULL, *tmp, *nvtop, *nvroot; - nvlist_t **spares; - uint_t i, nspares; + nvlist_t **spares, **l2cache; + uint_t i, nspares, nl2cache; boolean_t config_seen; uint64_t best_txg; char *name, *hostname; - zfs_cmd_t zc = { 0 }; uint64_t version, guid; - size_t len; - int err; uint_t children = 0; nvlist_t **child = NULL; uint_t c; boolean_t isactive; uint64_t hostid; + nvlist_t *nvl; + boolean_t found_one = B_FALSE; if (nvlist_alloc(&ret, 0, 0) != 0) goto nomem; @@ -571,6 +612,13 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl) nvlist_free(nvroot); /* + * zdb uses this path to report on active pools that were + * imported or created using -R. + */ + if (active_ok) + goto add_pool; + + /* * Determine if this pool is currently active, in which case we * can't actually import it. */ @@ -588,41 +636,11 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl) continue; } - /* - * Try to do the import in order to get vdev state. - */ - if (zcmd_write_src_nvlist(hdl, &zc, config, &len) != 0) + if ((nvl = refresh_config(hdl, config)) == NULL) goto error; nvlist_free(config); - config = NULL; - - if (zcmd_alloc_dst_nvlist(hdl, &zc, len * 2) != 0) { - zcmd_free_nvlists(&zc); - goto error; - } - - while ((err = ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_TRYIMPORT, - &zc)) != 0 && errno == ENOMEM) { - if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) { - zcmd_free_nvlists(&zc); - goto error; - } - } - - if (err) { - (void) zpool_standard_error(hdl, errno, - dgettext(TEXT_DOMAIN, "cannot discover pools")); - zcmd_free_nvlists(&zc); - goto error; - } - - if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) { - zcmd_free_nvlists(&zc); - goto error; - } - - zcmd_free_nvlists(&zc); + config = nvl; /* * Go through and update the paths for spares, now that we have @@ -639,6 +657,17 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl) } /* + * Update the paths for l2cache devices. + */ + if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE, + &l2cache, &nl2cache) == 0) { + for (i = 0; i < nl2cache; i++) { + if (fix_paths(l2cache[i], pl->names) != 0) + goto nomem; + } + } + + /* * Restore the original information read from the actual label. */ (void) nvlist_remove(config, ZPOOL_CONFIG_HOSTID, @@ -652,6 +681,7 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl) hostname) == 0); } +add_pool: /* * Add this pool to the list of configs. */ @@ -660,10 +690,16 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl) if (nvlist_add_nvlist(ret, name, config) != 0) goto nomem; + found_one = B_TRUE; nvlist_free(config); config = NULL; } + if (!found_one) { + nvlist_free(ret); + ret = NULL; + } + return (ret); nomem: @@ -682,8 +718,9 @@ error: * Return the offset of the given label. */ static uint64_t -label_offset(size_t size, int l) +label_offset(uint64_t size, int l) { + ASSERT(P2PHASE_TYPED(size, sizeof (vdev_label_t), uint64_t) == 0); return (l * sizeof (vdev_label_t) + (l < VDEV_LABELS / 2 ? 0 : size - VDEV_LABELS * sizeof (vdev_label_t))); } @@ -698,19 +735,20 @@ zpool_read_label(int fd, nvlist_t **config) struct stat64 statbuf; int l; vdev_label_t *label; - uint64_t state, txg; + uint64_t state, txg, size; *config = NULL; if (fstat64(fd, &statbuf) == -1) return (0); + size = P2ALIGN_TYPED(statbuf.st_size, sizeof (vdev_label_t), uint64_t); if ((label = malloc(sizeof (vdev_label_t))) == NULL) return (-1); for (l = 0; l < VDEV_LABELS; l++) { - if (pread(fd, label, sizeof (vdev_label_t), - label_offset(statbuf.st_size, l)) != sizeof (vdev_label_t)) + if (pread64(fd, label, sizeof (vdev_label_t), + label_offset(size, l)) != sizeof (vdev_label_t)) continue; if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist, @@ -718,12 +756,12 @@ zpool_read_label(int fd, nvlist_t **config) continue; if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_STATE, - &state) != 0 || state > POOL_STATE_SPARE) { + &state) != 0 || state > POOL_STATE_L2CACHE) { nvlist_free(*config); continue; } - if (state != POOL_STATE_SPARE && + if (state != POOL_STATE_SPARE && state != POOL_STATE_L2CACHE && (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_TXG, &txg) != 0 || txg == 0)) { nvlist_free(*config); @@ -739,31 +777,20 @@ zpool_read_label(int fd, nvlist_t **config) return (0); } -/* - * Given a list of directories to search, find all pools stored on disk. This - * includes partial pools which are not available to import. If no args are - * given (argc is 0), then the default directory (/dev) is searched. - */ -nvlist_t * -zpool_find_import(libzfs_handle_t *hdl, int argc, char **argv) +static int +geom_find_import(libzfs_handle_t *hdl, pool_list_t *pools) { - int i; char path[MAXPATHLEN]; - nvlist_t *ret = NULL, *config; - int fd; - pool_list_t pools = { 0 }; - pool_entry_t *pe, *penext; - vdev_entry_t *ve, *venext; - config_entry_t *ce, *cenext; - name_entry_t *ne, *nenext; struct gmesh mesh; struct gclass *mp; struct ggeom *gp; struct gprovider *pp; + nvlist_t *config; + int fd, ret = 0; /* * Go through and read the label configuration information from every - * possible device, organizing the information according to pool GUID + * GEOM provider, organizing the information according to pool GUID * and toplevel GUID. */ @@ -773,32 +800,183 @@ zpool_find_import(libzfs_handle_t *hdl, int argc, char **argv) LIST_FOREACH(mp, &mesh.lg_class, lg_class) { LIST_FOREACH(gp, &mp->lg_geom, lg_geom) { LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { + if ((fd = g_open(pp->lg_name, 0)) < 0) + continue; (void) snprintf(path, sizeof (path), "%s%s", _PATH_DEV, pp->lg_name); - if ((fd = open64(path, O_RDONLY)) < 0) - continue; - if ((zpool_read_label(fd, &config)) != 0) { + (void) g_close(fd); (void) no_memory(hdl); goto error; } - (void) close(fd); + (void) g_close(fd); if (config == NULL) continue; - if (add_config(hdl, &pools, path, config) != 0) + if (add_config(hdl, pools, path, config) != 0) { + ret = -1; goto error; + } } } } - +error: geom_deletetree(&mesh); + return (ret); +} + +/* + * Given a list of directories to search, find all pools stored on disk. This + * includes partial pools which are not available to import. If no args are + * given (argc is 0), then the default directory (/dev/dsk) is searched. + * poolname or guid (but not both) are provided by the caller when trying + * to import a specific pool. + */ +static nvlist_t * +zpool_find_import_impl(libzfs_handle_t *hdl, int argc, char **argv, + boolean_t active_ok, char *poolname, uint64_t guid) +{ + int i; + DIR *dirp = NULL; + struct dirent64 *dp; + char path[MAXPATHLEN]; + char *end; + size_t pathleft; + struct stat64 statbuf; + nvlist_t *ret = NULL, *config; + static char *default_dir = "/dev/dsk"; + int fd; + pool_list_t pools = { 0 }; + pool_entry_t *pe, *penext; + vdev_entry_t *ve, *venext; + config_entry_t *ce, *cenext; + name_entry_t *ne, *nenext; + + verify(poolname == NULL || guid == 0); + + if (argc == 0) { + argc = 1; + argv = &default_dir; + } + + /* + * Go through and read the label configuration information from every + * possible device, organizing the information according to pool GUID + * and toplevel GUID. + */ + for (i = 0; i < argc; i++) { + char *rdsk; + int dfd; + + /* use realpath to normalize the path */ + if (realpath(argv[i], path) == 0) { + (void) zfs_error_fmt(hdl, EZFS_BADPATH, + dgettext(TEXT_DOMAIN, "cannot open '%s'"), + argv[i]); + goto error; + } + end = &path[strlen(path)]; + *end++ = '/'; + *end = 0; + pathleft = &path[sizeof (path)] - end; - ret = get_configs(hdl, &pools); + if (strcmp(argv[i], default_dir) == 0) { + geom_find_import(hdl, &pools); + continue; + } + + /* + * Using raw devices instead of block devices when we're + * reading the labels skips a bunch of slow operations during + * close(2) processing, so we replace /dev/dsk with /dev/rdsk. + */ + if (strcmp(path, "/dev/dsk/") == 0) + rdsk = "/dev/rdsk/"; + else + rdsk = path; + + if ((dirp = opendir(rdsk)) == NULL) { + zfs_error_aux(hdl, strerror(errno)); + (void) zfs_error_fmt(hdl, EZFS_BADPATH, + dgettext(TEXT_DOMAIN, "cannot open '%s'"), + rdsk); + goto error; + } + + /* + * This is not MT-safe, but we have no MT consumers of libzfs + */ + while ((dp = readdir64(dirp)) != NULL) { + const char *name = dp->d_name; + if (name[0] == '.' && + (name[1] == 0 || (name[1] == '.' && name[2] == 0))) + continue; + + (void) snprintf(path, sizeof (path), "%s/%s", rdsk, + dp->d_name); + + if ((fd = open64(path, O_RDONLY)) < 0) + continue; + + /* + * Ignore failed stats. We only want regular + * files, character devs and block devs. + */ + if (fstat64(fd, &statbuf) != 0 || + (!S_ISREG(statbuf.st_mode) && + !S_ISCHR(statbuf.st_mode) && + !S_ISBLK(statbuf.st_mode))) { + (void) close(fd); + continue; + } + + if ((zpool_read_label(fd, &config)) != 0) { + (void) close(fd); + (void) no_memory(hdl); + goto error; + } + + (void) close(fd); + + if (config != NULL) { + boolean_t matched = B_TRUE; + + if (poolname != NULL) { + char *pname; + + matched = nvlist_lookup_string(config, + ZPOOL_CONFIG_POOL_NAME, + &pname) == 0 && + strcmp(poolname, pname) == 0; + } else if (guid != 0) { + uint64_t this_guid; + + matched = nvlist_lookup_uint64(config, + ZPOOL_CONFIG_POOL_GUID, + &this_guid) == 0 && + guid == this_guid; + } + if (!matched) { + nvlist_free(config); + config = NULL; + continue; + } + /* use the non-raw path for the config */ + (void) strlcpy(end, name, pathleft); + if (add_config(hdl, &pools, path, config) != 0) + goto error; + } + } + + (void) closedir(dirp); + dirp = NULL; + } + + ret = get_configs(hdl, &pools, active_ok); error: for (pe = pools.pools; pe != NULL; pe = penext) { @@ -823,9 +1001,158 @@ error: free(ne); } + if (dirp) + (void) closedir(dirp); + return (ret); } +nvlist_t * +zpool_find_import(libzfs_handle_t *hdl, int argc, char **argv) +{ + return (zpool_find_import_impl(hdl, argc, argv, B_FALSE, NULL, 0)); +} + +nvlist_t * +zpool_find_import_byname(libzfs_handle_t *hdl, int argc, char **argv, + char *pool) +{ + return (zpool_find_import_impl(hdl, argc, argv, B_FALSE, pool, 0)); +} + +nvlist_t * +zpool_find_import_byguid(libzfs_handle_t *hdl, int argc, char **argv, + uint64_t guid) +{ + return (zpool_find_import_impl(hdl, argc, argv, B_FALSE, NULL, guid)); +} + +nvlist_t * +zpool_find_import_activeok(libzfs_handle_t *hdl, int argc, char **argv) +{ + return (zpool_find_import_impl(hdl, argc, argv, B_TRUE, NULL, 0)); +} + +/* + * Given a cache file, return the contents as a list of importable pools. + * poolname or guid (but not both) are provided by the caller when trying + * to import a specific pool. + */ +nvlist_t * +zpool_find_import_cached(libzfs_handle_t *hdl, const char *cachefile, + char *poolname, uint64_t guid) +{ + char *buf; + int fd; + struct stat64 statbuf; + nvlist_t *raw, *src, *dst; + nvlist_t *pools; + nvpair_t *elem; + char *name; + uint64_t this_guid; + boolean_t active; + + verify(poolname == NULL || guid == 0); + + if ((fd = open(cachefile, O_RDONLY)) < 0) { + zfs_error_aux(hdl, "%s", strerror(errno)); + (void) zfs_error(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, "failed to open cache file")); + return (NULL); + } + + if (fstat64(fd, &statbuf) != 0) { + zfs_error_aux(hdl, "%s", strerror(errno)); + (void) close(fd); + (void) zfs_error(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, "failed to get size of cache file")); + return (NULL); + } + + if ((buf = zfs_alloc(hdl, statbuf.st_size)) == NULL) { + (void) close(fd); + return (NULL); + } + + if (read(fd, buf, statbuf.st_size) != statbuf.st_size) { + (void) close(fd); + free(buf); + (void) zfs_error(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, + "failed to read cache file contents")); + return (NULL); + } + + (void) close(fd); + + if (nvlist_unpack(buf, statbuf.st_size, &raw, 0) != 0) { + free(buf); + (void) zfs_error(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, + "invalid or corrupt cache file contents")); + return (NULL); + } + + free(buf); + + /* + * Go through and get the current state of the pools and refresh their + * state. + */ + if (nvlist_alloc(&pools, 0, 0) != 0) { + (void) no_memory(hdl); + nvlist_free(raw); + return (NULL); + } + + elem = NULL; + while ((elem = nvlist_next_nvpair(raw, elem)) != NULL) { + verify(nvpair_value_nvlist(elem, &src) == 0); + + verify(nvlist_lookup_string(src, ZPOOL_CONFIG_POOL_NAME, + &name) == 0); + if (poolname != NULL && strcmp(poolname, name) != 0) + continue; + + verify(nvlist_lookup_uint64(src, ZPOOL_CONFIG_POOL_GUID, + &this_guid) == 0); + if (guid != 0) { + verify(nvlist_lookup_uint64(src, ZPOOL_CONFIG_POOL_GUID, + &this_guid) == 0); + if (guid != this_guid) + continue; + } + + if (pool_active(hdl, name, this_guid, &active) != 0) { + nvlist_free(raw); + nvlist_free(pools); + return (NULL); + } + + if (active) + continue; + + if ((dst = refresh_config(hdl, src)) == NULL) { + nvlist_free(raw); + nvlist_free(pools); + return (NULL); + } + + if (nvlist_add_nvlist(pools, nvpair_name(elem), dst) != 0) { + (void) no_memory(hdl); + nvlist_free(dst); + nvlist_free(raw); + nvlist_free(pools); + return (NULL); + } + nvlist_free(dst); + } + + nvlist_free(raw); + return (pools); +} + + boolean_t find_guid(nvlist_t *nv, uint64_t guid) { @@ -847,27 +1174,28 @@ find_guid(nvlist_t *nv, uint64_t guid) return (B_FALSE); } -typedef struct spare_cbdata { +typedef struct aux_cbdata { + const char *cb_type; uint64_t cb_guid; zpool_handle_t *cb_zhp; -} spare_cbdata_t; +} aux_cbdata_t; static int -find_spare(zpool_handle_t *zhp, void *data) +find_aux(zpool_handle_t *zhp, void *data) { - spare_cbdata_t *cbp = data; - nvlist_t **spares; - uint_t i, nspares; + aux_cbdata_t *cbp = data; + nvlist_t **list; + uint_t i, count; uint64_t guid; nvlist_t *nvroot; verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0); - if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, - &spares, &nspares) == 0) { - for (i = 0; i < nspares; i++) { - verify(nvlist_lookup_uint64(spares[i], + if (nvlist_lookup_nvlist_array(nvroot, cbp->cb_type, + &list, &count) == 0) { + for (i = 0; i < count; i++) { + verify(nvlist_lookup_uint64(list[i], ZPOOL_CONFIG_GUID, &guid) == 0); if (guid == cbp->cb_guid) { cbp->cb_zhp = zhp; @@ -896,7 +1224,7 @@ zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr, zpool_handle_t *zhp; nvlist_t *pool_config; uint64_t stateval, isspare; - spare_cbdata_t cb = { 0 }; + aux_cbdata_t cb = { 0 }; boolean_t isactive; *inuse = B_FALSE; @@ -914,7 +1242,7 @@ zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr, verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, &vdev_guid) == 0); - if (stateval != POOL_STATE_SPARE) { + if (stateval != POOL_STATE_SPARE && stateval != POOL_STATE_L2CACHE) { verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, &name) == 0); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, @@ -993,7 +1321,24 @@ zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr, */ cb.cb_zhp = NULL; cb.cb_guid = vdev_guid; - if (zpool_iter(hdl, find_spare, &cb) == 1) { + cb.cb_type = ZPOOL_CONFIG_SPARES; + if (zpool_iter(hdl, find_aux, &cb) == 1) { + name = (char *)zpool_get_name(cb.cb_zhp); + ret = TRUE; + } else { + ret = FALSE; + } + break; + + case POOL_STATE_L2CACHE: + + /* + * Check if any pool is currently using this l2cache device. + */ + cb.cb_zhp = NULL; + cb.cb_guid = vdev_guid; + cb.cb_type = ZPOOL_CONFIG_L2CACHE; + if (zpool_iter(hdl, find_aux, &cb) == 1) { name = (char *)zpool_get_name(cb.cb_zhp); ret = TRUE; } else { @@ -1008,6 +1353,8 @@ zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr, if (ret) { if ((*namestr = zfs_strdup(hdl, name)) == NULL) { + if (cb.cb_zhp) + zpool_close(cb.cb_zhp); nvlist_free(config); return (-1); } |