X-Git-Url: https://git.camperquake.de/gitweb.cgi?a=blobdiff_plain;f=lib%2Flibzfs%2Flibzfs_import.c;h=7048a52c9ced6f4bc80652f26297245364fe685f;hb=ea1fdf46e2d37e9eb048f9add0fb06e77e9ee6e2;hp=d67776889d3501faecf2ccddefb025cc21967f41;hpb=172bb4bd5e4afef721dd4d2972d8680d983f144b;p=zfs.git diff --git a/lib/libzfs/libzfs_import.c b/lib/libzfs/libzfs_import.c index d677768..7048a52 100644 --- a/lib/libzfs/libzfs_import.c +++ b/lib/libzfs/libzfs_import.c @@ -19,12 +19,9 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Pool import support functions. * @@ -41,17 +38,25 @@ * using our derived config, and record the results. */ +#include #include #include #include #include +#include #include #include #include #include #include +#include +#include +#include #include +#ifdef HAVE_LIBBLKID +#include +#endif #include "libzfs.h" #include "libzfs_impl.h" @@ -367,7 +372,7 @@ static nvlist_t * refresh_config(libzfs_handle_t *hdl, nvlist_t *config) { nvlist_t *nvl; - zfs_cmd_t zc = { 0 }; + zfs_cmd_t zc = { "\0", "\0", "\0", "\0", 0 }; int err; if (zcmd_write_conf_nvlist(hdl, &zc, config) != 0) @@ -388,8 +393,6 @@ refresh_config(libzfs_handle_t *hdl, nvlist_t *config) } if (err) { - (void) zpool_standard_error(hdl, errno, - dgettext(TEXT_DOMAIN, "cannot discover pools")); zcmd_free_nvlists(&zc); return (NULL); } @@ -404,6 +407,23 @@ refresh_config(libzfs_handle_t *hdl, nvlist_t *config) } /* + * Determine if the vdev id is a hole in the namespace. + */ +boolean_t +vdev_is_hole(uint64_t *hole_array, uint_t holes, uint_t id) +{ + int c; + + for (c = 0; c < holes; c++) { + + /* Top-level is a hole */ + if (hole_array[c] == id) + return (B_TRUE); + } + return (B_FALSE); +} + +/* * 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, * we assemble the toplevel vdevs into a full config for the pool. We make a @@ -416,7 +436,7 @@ 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 *ret = NULL, *config = NULL, *tmp = NULL, *nvtop, *nvroot; nvlist_t **spares, **l2cache; uint_t i, nspares, nl2cache; boolean_t config_seen; @@ -425,17 +445,20 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok) uint64_t version, guid; uint_t children = 0; nvlist_t **child = NULL; + uint_t holes; + uint64_t *hole_array, max_id; uint_t c; boolean_t isactive; uint64_t hostid; nvlist_t *nvl; boolean_t found_one = B_FALSE; + boolean_t valid_top_config = B_FALSE; if (nvlist_alloc(&ret, 0, 0) != 0) goto nomem; for (pe = pl->pools; pe != NULL; pe = pe->pe_next) { - uint64_t id; + uint64_t id, max_txg = 0; if (nvlist_alloc(&config, NV_UNIQUE_NAME, 0) != 0) goto nomem; @@ -463,6 +486,42 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok) } } + /* + * We rely on the fact that the max txg for the + * pool will contain the most up-to-date information + * about the valid top-levels in the vdev namespace. + */ + if (best_txg > max_txg) { + (void) nvlist_remove(config, + ZPOOL_CONFIG_VDEV_CHILDREN, + DATA_TYPE_UINT64); + (void) nvlist_remove(config, + ZPOOL_CONFIG_HOLE_ARRAY, + DATA_TYPE_UINT64_ARRAY); + + max_txg = best_txg; + hole_array = NULL; + holes = 0; + max_id = 0; + valid_top_config = B_FALSE; + + if (nvlist_lookup_uint64(tmp, + ZPOOL_CONFIG_VDEV_CHILDREN, &max_id) == 0) { + verify(nvlist_add_uint64(config, + ZPOOL_CONFIG_VDEV_CHILDREN, + max_id) == 0); + valid_top_config = B_TRUE; + } + + if (nvlist_lookup_uint64_array(tmp, + ZPOOL_CONFIG_HOLE_ARRAY, &hole_array, + &holes) == 0) { + verify(nvlist_add_uint64_array(config, + ZPOOL_CONFIG_HOLE_ARRAY, + hole_array, holes) == 0); + } + } + if (!config_seen) { /* * Copy the relevant pieces of data to the pool @@ -522,6 +581,7 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok) ZPOOL_CONFIG_VDEV_TREE, &nvtop) == 0); verify(nvlist_lookup_uint64(nvtop, ZPOOL_CONFIG_ID, &id) == 0); + if (id >= children) { nvlist_t **newchild; @@ -542,17 +602,82 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok) } + /* + * If we have information about all the top-levels then + * clean up the nvlist which we've constructed. This + * means removing any extraneous devices that are + * beyond the valid range or adding devices to the end + * of our array which appear to be missing. + */ + if (valid_top_config) { + if (max_id < children) { + for (c = max_id; c < children; c++) + nvlist_free(child[c]); + children = max_id; + } else if (max_id > children) { + nvlist_t **newchild; + + newchild = zfs_alloc(hdl, (max_id) * + sizeof (nvlist_t *)); + if (newchild == NULL) + goto nomem; + + for (c = 0; c < children; c++) + newchild[c] = child[c]; + + free(child); + child = newchild; + children = max_id; + } + } + verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) == 0); /* + * The vdev namespace may contain holes as a result of + * device removal. We must add them back into the vdev + * tree before we process any missing devices. + */ + if (holes > 0) { + ASSERT(valid_top_config); + + for (c = 0; c < children; c++) { + nvlist_t *holey; + + if (child[c] != NULL || + !vdev_is_hole(hole_array, holes, c)) + continue; + + if (nvlist_alloc(&holey, NV_UNIQUE_NAME, + 0) != 0) + goto nomem; + + /* + * Holes in the namespace are treated as + * "hole" top-level vdevs and have a + * special flag set on them. + */ + if (nvlist_add_string(holey, + ZPOOL_CONFIG_TYPE, + VDEV_TYPE_HOLE) != 0 || + nvlist_add_uint64(holey, + ZPOOL_CONFIG_ID, c) != 0 || + nvlist_add_uint64(holey, + ZPOOL_CONFIG_GUID, 0ULL) != 0) + goto nomem; + child[c] = holey; + } + } + + /* * Look for any missing top-level vdevs. If this is the case, * create a faked up 'missing' vdev as a placeholder. We cannot * simply compress the child array, because the kernel performs * certain checks to make sure the vdev IDs match their location * in the configuration. */ - for (c = 0; c < children; c++) + for (c = 0; c < children; c++) { if (child[c] == NULL) { nvlist_t *missing; if (nvlist_alloc(&missing, NV_UNIQUE_NAME, @@ -570,6 +695,7 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok) } child[c] = missing; } + } /* * Put all of this pool's top-level vdevs into a root vdev. @@ -636,8 +762,11 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok) continue; } - if ((nvl = refresh_config(hdl, config)) == NULL) - goto error; + if ((nvl = refresh_config(hdl, config)) == NULL) { + nvlist_free(config); + config = NULL; + continue; + } nvlist_free(config); config = nvl; @@ -777,6 +906,77 @@ zpool_read_label(int fd, nvlist_t **config) return (0); } +#ifdef HAVE_LIBBLKID +/* + * Use libblkid to quickly search for zfs devices + */ +static int +zpool_find_import_blkid(libzfs_handle_t *hdl, pool_list_t *pools) +{ + blkid_cache cache; + blkid_dev_iterate iter; + blkid_dev dev; + const char *devname; + nvlist_t *config; + int fd, err; + + err = blkid_get_cache(&cache, NULL); + if (err != 0) { + (void) zfs_error_fmt(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, "blkid_get_cache() %d"), err); + goto err_blkid1; + } + + err = blkid_probe_all(cache); + if (err != 0) { + (void) zfs_error_fmt(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, "blkid_probe_all() %d"), err); + goto err_blkid2; + } + + iter = blkid_dev_iterate_begin(cache); + if (iter == NULL) { + (void) zfs_error_fmt(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, "blkid_dev_iterate_begin()")); + goto err_blkid2; + } + + err = blkid_dev_set_search(iter, "TYPE", "zfs"); + if (err != 0) { + (void) zfs_error_fmt(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, "blkid_dev_set_search() %d"), err); + goto err_blkid3; + } + + while (blkid_dev_next(iter, &dev) == 0) { + devname = blkid_dev_devname(dev); + if ((fd = open64(devname, O_RDONLY)) < 0) + continue; + + err = zpool_read_label(fd, &config); + (void) close(fd); + + if (err != 0) { + (void) no_memory(hdl); + goto err_blkid3; + } + + if (config != NULL) { + err = add_config(hdl, pools, devname, config); + if (err != 0) + goto err_blkid3; + } + } + +err_blkid3: + blkid_dev_iterate_end(iter); +err_blkid2: + blkid_put_cache(cache); +err_blkid1: + return err; +} +#endif /* HAVE_LIBBLKID */ + /* * 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 @@ -785,18 +985,17 @@ zpool_read_label(int fd, nvlist_t **config) * 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) +zpool_find_import_impl(libzfs_handle_t *hdl, importargs_t *iarg) { - int i; + int i, dirs = iarg->paths; DIR *dirp = NULL; struct dirent64 *dp; char path[MAXPATHLEN]; - char *end; + char *end, **dir = iarg->path; size_t pathleft; struct stat64 statbuf; nvlist_t *ret = NULL, *config; - static char *default_dir = "/dev/dsk"; + static char *default_dir = DISK_ROOT; int fd; pool_list_t pools = { 0 }; pool_entry_t *pe, *penext; @@ -804,11 +1003,20 @@ zpool_find_import_impl(libzfs_handle_t *hdl, int argc, char **argv, config_entry_t *ce, *cenext; name_entry_t *ne, *nenext; - verify(poolname == NULL || guid == 0); + verify(iarg->poolname == NULL || iarg->guid == 0); - if (argc == 0) { - argc = 1; - argv = &default_dir; + if (dirs == 0) { +#ifdef HAVE_LIBBLKID + /* Use libblkid to scan all device for their type */ + if (zpool_find_import_blkid(hdl, &pools) == 0) + goto skip_scanning; + + (void) zfs_error_fmt(hdl, EZFS_BADCACHE, + dgettext(TEXT_DOMAIN, "blkid failure falling back " + "to manual probing")); +#endif /* HAVE_LIBBLKID */ + dirs = 1; + dir = &default_dir; } /* @@ -816,15 +1024,14 @@ zpool_find_import_impl(libzfs_handle_t *hdl, int argc, char **argv, * possible device, organizing the information according to pool GUID * and toplevel GUID. */ - for (i = 0; i < argc; i++) { + for (i = 0; i < dirs; i++) { char *rdsk; int dfd; /* use realpath to normalize the path */ - if (realpath(argv[i], path) == 0) { + if (realpath(dir[i], path) == 0) { (void) zfs_error_fmt(hdl, EZFS_BADPATH, - dgettext(TEXT_DOMAIN, "cannot open '%s'"), - argv[i]); + dgettext(TEXT_DOMAIN, "cannot open '%s'"), dir[i]); goto error; } end = &path[strlen(path)]; @@ -860,20 +1067,45 @@ zpool_find_import_impl(libzfs_handle_t *hdl, int argc, char **argv, (name[1] == 0 || (name[1] == '.' && name[2] == 0))) continue; - if ((fd = openat64(dfd, name, O_RDONLY)) < 0) + /* + * Skip checking devices with well known prefixes: + * watchdog - A special close is required to avoid + * triggering it and resetting the system. + * fuse - Fuse control device. + * ppp - Generic PPP driver. + * tty* - Generic serial interface. + * vcs* - Virtual console memory. + * parport* - Parallel port interface. + * lp* - Printer interface. + * fd* - Floppy interface. + * hpet - High Precision Event Timer, crashes qemu + * when accessed from a virtual machine. + * core - Symlink to /proc/kcore, causes a crash + * when access from Xen dom0. + */ + if ((strncmp(name, "watchdog", 8) == 0) || + (strncmp(name, "fuse", 4) == 0) || + (strncmp(name, "ppp", 3) == 0) || + (strncmp(name, "tty", 3) == 0) || + (strncmp(name, "vcs", 3) == 0) || + (strncmp(name, "parport", 7) == 0) || + (strncmp(name, "lp", 2) == 0) || + (strncmp(name, "fd", 2) == 0) || + (strncmp(name, "hpet", 4) == 0) || + (strncmp(name, "core", 4) == 0)) continue; /* * Ignore failed stats. We only want regular - * files, character devs and block devs. + * files and block devices. */ - if (fstat64(fd, &statbuf) != 0 || + if ((fstatat64(dfd, name, &statbuf, 0) != 0) || (!S_ISREG(statbuf.st_mode) && - !S_ISCHR(statbuf.st_mode) && - !S_ISBLK(statbuf.st_mode))) { - (void) close(fd); + !S_ISBLK(statbuf.st_mode))) + continue; + + if ((fd = openat64(dfd, name, O_RDONLY)) < 0) continue; - } if ((zpool_read_label(fd, &config)) != 0) { (void) close(fd); @@ -886,20 +1118,20 @@ zpool_find_import_impl(libzfs_handle_t *hdl, int argc, char **argv, if (config != NULL) { boolean_t matched = B_TRUE; - if (poolname != NULL) { + if (iarg->poolname != NULL) { char *pname; matched = nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, &pname) == 0 && - strcmp(poolname, pname) == 0; - } else if (guid != 0) { + strcmp(iarg->poolname, pname) == 0; + } else if (iarg->guid != 0) { uint64_t this_guid; matched = nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &this_guid) == 0 && - guid == this_guid; + iarg->guid == this_guid; } if (!matched) { nvlist_free(config); @@ -917,7 +1149,10 @@ zpool_find_import_impl(libzfs_handle_t *hdl, int argc, char **argv, dirp = NULL; } - ret = get_configs(hdl, &pools, active_ok); +#ifdef HAVE_LIBBLKID +skip_scanning: +#endif + ret = get_configs(hdl, &pools, iarg->can_be_active); error: for (pe = pools.pools; pe != NULL; pe = penext) { @@ -951,27 +1186,12 @@ error: 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)); -} + importargs_t iarg = { 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)); -} + iarg.paths = argc; + iarg.path = argv; -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)); + return (zpool_find_import_impl(hdl, &iarg)); } /* @@ -1093,6 +1313,46 @@ zpool_find_import_cached(libzfs_handle_t *hdl, const char *cachefile, return (pools); } +static int +name_or_guid_exists(zpool_handle_t *zhp, void *data) +{ + importargs_t *import = data; + int found = 0; + + if (import->poolname != NULL) { + char *pool_name; + + verify(nvlist_lookup_string(zhp->zpool_config, + ZPOOL_CONFIG_POOL_NAME, &pool_name) == 0); + if (strcmp(pool_name, import->poolname) == 0) + found = 1; + } else { + uint64_t pool_guid; + + verify(nvlist_lookup_uint64(zhp->zpool_config, + ZPOOL_CONFIG_POOL_GUID, &pool_guid) == 0); + if (pool_guid == import->guid) + found = 1; + } + + zpool_close(zhp); + return (found); +} + +nvlist_t * +zpool_search_import(libzfs_handle_t *hdl, importargs_t *import) +{ + verify(import->poolname == NULL || import->guid == 0); + + if (import->unique) + import->exists = zpool_iter(hdl, name_or_guid_exists, import); + + if (import->cachefile != NULL) + return (zpool_find_import_cached(hdl, import->cachefile, + import->poolname, import->guid)); + + return (zpool_find_import_impl(hdl, import)); +} boolean_t find_guid(nvlist_t *nv, uint64_t guid) @@ -1192,6 +1452,17 @@ zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr, switch (stateval) { case POOL_STATE_EXPORTED: + /* + * A pool with an exported state may in fact be imported + * read-only, so check the in-core state to see if it's + * active and imported read-only. If it is, set + * its state to active. + */ + if (pool_active(hdl, name, guid, &isactive) == 0 && isactive && + (zhp = zpool_open_canfail(hdl, name)) != NULL && + zpool_get_prop_int(zhp, ZPOOL_PROP_READONLY, NULL)) + stateval = POOL_STATE_ACTIVE; + ret = B_TRUE; break;