#include <dirent.h>
#include <errno.h>
#include <libintl.h>
+#include <libgen.h>
#ifdef HAVE_LIBUDEV
#include <libudev.h>
#include <sched.h>
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.
*/
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;
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;
.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
.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
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
.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
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
.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
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
--- /dev/null
+#!/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>'"