* 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.
*
* using our derived config, and record the results.
*/
+#include <ctype.h>
#include <devid.h>
#include <dirent.h>
#include <errno.h>
#include <libintl.h>
+#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
+#include <sys/vtoc.h>
+#include <sys/dktp/fdisk.h>
+#include <sys/efi_partition.h>
#include <sys/vdev_impl.h>
+#ifdef HAVE_LIBBLKID
+#include <blkid/blkid.h>
+#endif
#include "libzfs.h"
#include "libzfs_impl.h"
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)
}
if (err) {
- (void) zpool_standard_error(hdl, errno,
- dgettext(TEXT_DOMAIN, "cannot discover pools"));
zcmd_free_nvlists(&zc);
return (NULL);
}
}
/*
+ * 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
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;
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;
}
}
+ /*
+ * 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
ZPOOL_CONFIG_VDEV_TREE, &nvtop) == 0);
verify(nvlist_lookup_uint64(nvtop, ZPOOL_CONFIG_ID,
&id) == 0);
+
if (id >= children) {
nvlist_t **newchild;
}
+ /*
+ * 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,
}
child[c] = missing;
}
+ }
/*
* Put all of this pool's top-level vdevs into a root vdev.
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;
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
* 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;
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;
}
/*
* 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)];
(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);
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);
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) {
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));
}
/*
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)
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;