zpool import -d to specify device path

When we know which devices have the pool we are looking for, sometime
it's better if we can directly pass those device paths to zpool import
instead of letting it to search through all unrelated stuff, which might
take a lot of time if you have hundreds of disks.

This patch allows option -d <dev_path> to zpool import. You can have
multiple pairs of -d <dev_path>, and zpool import will only search
through those devices. For example:

    zpool import -d /dev/sda -d /dev/sdb

Reviewed-by: Tony Hutter <hutter2@llnl.gov>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Chunwei Chen <david.chen@nutanix.com>
Closes #7077
This commit is contained in:
Chunwei Chen
2018-01-26 10:49:46 -08:00
committed by Brian Behlendorf
parent f55a8757a6
commit 522db29275
6 changed files with 205 additions and 48 deletions
+1
View File
@@ -28,6 +28,7 @@
#define _LIBSPL_LIBGEN_H
#include <sys/types.h>
#include_next <libgen.h>
extern int mkdirp(const char *, mode_t);
+130 -36
View File
@@ -47,6 +47,7 @@
#include <dirent.h>
#include <errno.h>
#include <libintl.h>
#include <libgen.h>
#ifdef HAVE_LIBUDEV
#include <libudev.h>
#include <sched.h>
@@ -1676,6 +1677,120 @@ zpool_clear_label(int fd)
return (0);
}
static void
zpool_find_import_scan_add_slice(libzfs_handle_t *hdl, pthread_mutex_t *lock,
avl_tree_t *cache, char *path, const char *name, int order)
{
avl_index_t where;
rdsk_node_t *slice;
slice = zfs_alloc(hdl, sizeof (rdsk_node_t));
if (asprintf(&slice->rn_name, "%s/%s", path, name) == -1) {
free(slice);
return;
}
slice->rn_vdev_guid = 0;
slice->rn_lock = lock;
slice->rn_avl = cache;
slice->rn_hdl = hdl;
slice->rn_order = order + IMPORT_ORDER_SCAN_OFFSET;
slice->rn_labelpaths = B_FALSE;
pthread_mutex_lock(lock);
if (avl_find(cache, slice, &where)) {
free(slice->rn_name);
free(slice);
} else {
avl_insert(cache, slice, where);
}
pthread_mutex_unlock(lock);
}
static int
zpool_find_import_scan_dir(libzfs_handle_t *hdl, pthread_mutex_t *lock,
avl_tree_t *cache, char *dir, int order)
{
int error;
char path[MAXPATHLEN];
struct dirent64 *dp;
DIR *dirp;
if (realpath(dir, path) == NULL) {
error = errno;
if (error == ENOENT)
return (0);
zfs_error_aux(hdl, strerror(error));
(void) zfs_error_fmt(hdl, EZFS_BADPATH, dgettext(
TEXT_DOMAIN, "cannot resolve path '%s'"), dir);
return (error);
}
dirp = opendir(path);
if (dirp == NULL) {
error = errno;
zfs_error_aux(hdl, strerror(error));
(void) zfs_error_fmt(hdl, EZFS_BADPATH,
dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
return (error);
}
while ((dp = readdir64(dirp)) != NULL) {
const char *name = dp->d_name;
if (name[0] == '.' &&
(name[1] == 0 || (name[1] == '.' && name[2] == 0)))
continue;
zpool_find_import_scan_add_slice(hdl, lock, cache, path, name,
order);
}
(void) closedir(dirp);
return (0);
}
static int
zpool_find_import_scan_path(libzfs_handle_t *hdl, pthread_mutex_t *lock,
avl_tree_t *cache, char *dir, int order)
{
int error = 0;
char path[MAXPATHLEN];
char *d, *b;
char *dpath, *name;
/*
* Seperate the directory part and last part of the
* path. We do this so that we can get the realpath of
* the directory. We don't get the realpath on the
* whole path because if it's a symlink, we want the
* path of the symlink not where it points to.
*/
d = zfs_strdup(hdl, dir);
b = zfs_strdup(hdl, dir);
dpath = dirname(d);
name = basename(b);
if (realpath(dpath, path) == NULL) {
error = errno;
if (error == ENOENT) {
error = 0;
goto out;
}
zfs_error_aux(hdl, strerror(error));
(void) zfs_error_fmt(hdl, EZFS_BADPATH, dgettext(
TEXT_DOMAIN, "cannot resolve path '%s'"), dir);
goto out;
}
zpool_find_import_scan_add_slice(hdl, lock, cache, path, name, order);
out:
free(b);
free(d);
return (error);
}
/*
* Scan a list of directories for zfs devices.
*/
@@ -1694,11 +1809,9 @@ zpool_find_import_scan(libzfs_handle_t *hdl, pthread_mutex_t *lock,
offsetof(rdsk_node_t, rn_node));
for (i = 0; i < dirs; i++) {
char path[MAXPATHLEN];
struct dirent64 *dp;
DIR *dirp;
struct stat sbuf;
if (realpath(dir[i], path) == NULL) {
if (stat(dir[i], &sbuf) != 0) {
error = errno;
if (error == ENOENT)
continue;
@@ -1709,39 +1822,20 @@ zpool_find_import_scan(libzfs_handle_t *hdl, pthread_mutex_t *lock,
goto error;
}
dirp = opendir(path);
if (dirp == NULL) {
error = errno;
zfs_error_aux(hdl, strerror(error));
(void) zfs_error_fmt(hdl, EZFS_BADPATH,
dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
goto error;
/*
* If dir[i] is a directory, we walk through it and add all
* the entry to the cache. If it's not a directory, we just
* add it to the cache.
*/
if (S_ISDIR(sbuf.st_mode)) {
if ((error = zpool_find_import_scan_dir(hdl, lock,
cache, dir[i], i)) != 0)
goto error;
} else {
if ((error = zpool_find_import_scan_path(hdl, lock,
cache, dir[i], i)) != 0)
goto error;
}
while ((dp = readdir64(dirp)) != NULL) {
const char *name = dp->d_name;
if (name[0] == '.' &&
(name[1] == 0 || (name[1] == '.' && name[2] == 0)))
continue;
slice = zfs_alloc(hdl, sizeof (rdsk_node_t));
error = asprintf(&slice->rn_name, "%s/%s", path, name);
if (error == -1) {
free(slice);
continue;
}
slice->rn_vdev_guid = 0;
slice->rn_lock = lock;
slice->rn_avl = cache;
slice->rn_hdl = hdl;
slice->rn_order = i + IMPORT_ORDER_SCAN_OFFSET;
slice->rn_labelpaths = B_FALSE;
pthread_mutex_lock(lock);
avl_add(cache, slice);
pthread_mutex_unlock(lock);
}
(void) closedir(dirp);
}
*slice_cache = cache;