]> granicus.if.org Git - zfs/commitdiff
zpool import -d to specify device path
authorChunwei Chen <tuxoko@gmail.com>
Fri, 26 Jan 2018 18:49:46 +0000 (10:49 -0800)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Fri, 26 Jan 2018 18:49:46 +0000 (10:49 -0800)
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

lib/libspl/include/libgen.h
lib/libzfs/libzfs_import.c
man/man8/zpool.8
tests/runfiles/linux.run
tests/zfs-tests/tests/functional/cli_root/zpool_import/Makefile.am
tests/zfs-tests/tests/functional/cli_root/zpool_import/zpool_import_015_pos.ksh [new file with mode: 0755]

index 7c03d81fda6dd5a647e074d4393c99ce94bdcef8..c46d7454e49f3b7bbe9bd9f84874b2034087efb0 100644 (file)
@@ -28,6 +28,7 @@
 #define        _LIBSPL_LIBGEN_H
 
 #include <sys/types.h>
+#include_next <libgen.h>
 
 extern int mkdirp(const char *, mode_t);
 
index 6203cd19bcd71652e4cf99a128731a5d2e573f2e..435d91fb83e38a90b78790d7b05039b13f4636f0 100644 (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;
-               }
-
-               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);
+               /*
+                * 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;
                }
-
-               (void) closedir(dirp);
        }
 
        *slice_cache = cache;
index 64f570d7be3f52b0f3d6afe8f9d8c188ff5867f7..c69ff8446dc932f95401e36d98e385b77d1ffd00 100644 (file)
 .Nm
 .Cm import
 .Op Fl D
-.Op Fl d Ar dir
+.Op Fl d Ar dir Ns | Ns device
 .Nm
 .Cm import
 .Fl a
 .Op Fl DflmN
 .Op Fl F Oo Fl n Oc Oo Fl T Oc Oo Fl X Oc
-.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir
+.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir Ns | Ns device
 .Op Fl o Ar mntopts
 .Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
 .Op Fl R Ar root
 .Cm import
 .Op Fl Dflm
 .Op Fl F Oo Fl n Oc Oo Fl T Oc Oo Fl X Oc
-.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir
+.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir Ns | Ns device
 .Op Fl o Ar mntopts
 .Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
 .Op Fl R Ar root
@@ -1137,7 +1137,7 @@ performed.
 .Nm
 .Cm import
 .Op Fl D
-.Op Fl d Ar dir
+.Op Fl d Ar dir Ns | Ns device
 .Xc
 Lists pools available to import.
 If the
@@ -1168,8 +1168,10 @@ pool property.
 This
 .Ar cachefile
 is used instead of searching for devices.
-.It Fl d Ar dir
-Searches for devices or files in
+.It Fl d Ar dir Ns | Ns Ar device
+Uses
+.Ar device
+or searches for devices or files in
 .Ar dir .
 The
 .Fl d
@@ -1183,7 +1185,7 @@ Lists destroyed pools only.
 .Fl a
 .Op Fl DflmN
 .Op Fl F Oo Fl n Oc Oo Fl T Oc Oo Fl X Oc
-.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir
+.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir Ns | Ns device
 .Op Fl o Ar mntopts
 .Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
 .Op Fl R Ar root
@@ -1209,8 +1211,10 @@ pool property.
 This
 .Ar cachefile
 is used instead of searching for devices.
-.It Fl d Ar dir
-Searches for devices or files in
+.It Fl d Ar dir Ns | Ns Ar device
+Uses
+.Ar device
+or searches for devices or files in
 .Ar dir .
 The
 .Fl d
@@ -1304,7 +1308,7 @@ health of your pool and should only be used as a last resort.
 .Cm import
 .Op Fl Dflm
 .Op Fl F Oo Fl n Oc Oo Fl t Oc Oo Fl T Oc Oo Fl X Oc
-.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir
+.Op Fl c Ar cachefile Ns | Ns Fl d Ar dir Ns | Ns device
 .Op Fl o Ar mntopts
 .Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
 .Op Fl R Ar root
@@ -1338,8 +1342,10 @@ pool property.
 This
 .Ar cachefile
 is used instead of searching for devices.
-.It Fl d Ar dir
-Searches for devices or files in
+.It Fl d Ar dir Ns | Ns Ar device
+Uses
+.Ar device
+or searches for devices or files in
 .Ar dir .
 The
 .Fl d
index af96e6a64eb90c03e453cda86acdc1625c319397..0819623cda771f24637210163464e76c996b1a51 100644 (file)
@@ -328,6 +328,7 @@ tests = ['zpool_import_001_pos', 'zpool_import_002_pos',
     'zpool_import_006_pos', 'zpool_import_007_pos', 'zpool_import_008_pos',
     'zpool_import_009_neg', 'zpool_import_010_pos', 'zpool_import_011_neg',
     'zpool_import_012_pos', 'zpool_import_013_neg', 'zpool_import_014_pos',
+    'zpool_import_015_pos',
     'zpool_import_features_001_pos', 'zpool_import_features_002_neg',
     'zpool_import_features_003_pos','zpool_import_missing_001_pos',
     'zpool_import_missing_002_pos',
index fab6e7459ffe1a0d851b7ccf41ac4dd7f33252c5..94031b9a70d0127c5497db20e19bc0b15d2ac7c8 100644 (file)
@@ -17,6 +17,7 @@ dist_pkgdata_SCRIPTS = \
        zpool_import_012_pos.ksh \
        zpool_import_013_neg.ksh \
        zpool_import_014_pos.ksh \
+       zpool_import_015_pos.ksh \
        zpool_import_all_001_pos.ksh \
        zpool_import_features_001_pos.ksh \
        zpool_import_features_002_neg.ksh \
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_import/zpool_import_015_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_import/zpool_import_015_pos.ksh
new file mode 100755 (executable)
index 0000000..c984b9b
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2018 by Nutanix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_import/zpool_import.cfg
+
+#
+# DESCRIPTION:
+#      Make sure zpool import -d <device> works.
+#
+# STRATEGY:
+#      1. Create test pool A.
+#      2. Export pool A.
+#      3. Verify 'import -d <device>' works
+#
+
+verify_runnable "global"
+
+function cleanup
+{
+       destroy_pool $TESTPOOL1
+
+       log_must rm $VDEV0 $VDEV1
+       log_must truncate -s $FILE_SIZE $VDEV0 $VDEV1
+}
+
+log_assert "Pool can be imported with '-d <device>'"
+log_onexit cleanup
+
+log_must zpool create $TESTPOOL1 $VDEV0 $VDEV1
+log_must zpool export $TESTPOOL1
+
+log_must zpool import -d $VDEV0 -d $VDEV1 $TESTPOOL1
+log_must zpool export $TESTPOOL1
+
+# mix -d <dir> and -d <device>
+log_must mkdir $DEVICE_DIR/test_dir
+log_must ln -s $VDEV0 $DEVICE_DIR/test_dir/disk
+log_must zpool import -d $DEVICE_DIR/test_dir -d $VDEV1 $TESTPOOL1
+
+log_pass "Pool can be imported with '-d <device>'"