summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/zpool/zpool_vdev.c28
-rw-r--r--include/libzfs.h22
-rw-r--r--lib/libzfs/libzfs_import.c4
-rw-r--r--lib/libzfs/libzfs_pool.c39
-rw-r--r--lib/libzfs/libzfs_util.c189
5 files changed, 186 insertions, 96 deletions
diff --git a/cmd/zpool/zpool_vdev.c b/cmd/zpool/zpool_vdev.c
index 8c4fadebd..da121f06f 100644
--- a/cmd/zpool/zpool_vdev.c
+++ b/cmd/zpool/zpool_vdev.c
@@ -366,16 +366,18 @@ is_whole_disk(const char *path)
/*
* This may be a shorthand device path or it could be total gibberish.
- * Check to see if it's a known device in /dev/, /dev/disk/by-id,
- * /dev/disk/by-label, /dev/disk/by-path, /dev/disk/by-uuid,
- * /dev/disk/by-vdev, or /dev/disk/zpool/. As part of this check, see
- * if we've been given an entire disk (minus the slice number).
+ * Check to see if it is a known device available in zfs_vdev_paths.
+ * As part of this check, see if we've been given an entire disk
+ * (minus the slice number).
*/
static int
is_shorthand_path(const char *arg, char *path,
struct stat64 *statbuf, boolean_t *wholedisk)
{
- if (zfs_resolve_shortname(arg, path, MAXPATHLEN) == 0) {
+ int error;
+
+ error = zfs_resolve_shortname(arg, path, MAXPATHLEN);
+ if (error == 0) {
*wholedisk = is_whole_disk(path);
if (*wholedisk || (stat64(path, statbuf) == 0))
return (0);
@@ -385,7 +387,7 @@ is_shorthand_path(const char *arg, char *path,
memset(statbuf, 0, sizeof(*statbuf));
*wholedisk = B_FALSE;
- return (ENOENT);
+ return (error);
}
/*
@@ -393,9 +395,9 @@ is_shorthand_path(const char *arg, char *path,
* device, fill in the device id to make a complete nvlist. Valid forms for a
* leaf vdev are:
*
- * /dev/xxx Complete disk path
- * /xxx Full path to file
- * xxx Shorthand for /dev/disk/yyy/xxx
+ * /dev/xxx Complete disk path
+ * /xxx Full path to file
+ * xxx Shorthand for <zfs_vdev_paths>/xxx
*/
static nvlist_t *
make_leaf_vdev(nvlist_t *props, const char *arg, uint64_t is_log)
@@ -959,7 +961,9 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv)
* deletes and recreates the link during which access attempts
* will fail with ENOENT.
*/
- zfs_append_partition(path, udevpath, sizeof (udevpath));
+ strncpy(udevpath, path, MAXPATHLEN);
+ (void) zfs_append_partition(udevpath, MAXPATHLEN);
+
if ((strncmp(udevpath, UDISK_ROOT, strlen(UDISK_ROOT)) == 0) &&
(lstat64(udevpath, &statbuf) == 0) &&
S_ISLNK(statbuf.st_mode))
@@ -983,9 +987,9 @@ make_disks(zpool_handle_t *zhp, nvlist_t *nv)
}
/*
- * Update the path to refer to FIRST_SLICE. The presence of
+ * Update the path to refer to the partition. The presence of
* the 'whole_disk' field indicates to the CLI that we should
- * chop off the slice number when displaying the device in
+ * chop off the partition number when displaying the device in
* future output.
*/
verify(nvlist_add_string(nv, ZPOOL_CONFIG_PATH, udevpath) == 0);
diff --git a/include/libzfs.h b/include/libzfs.h
index 75e149334..e59350c9d 100644
--- a/include/libzfs.h
+++ b/include/libzfs.h
@@ -54,22 +54,11 @@ extern "C" {
/*
* Default device paths
*/
+#define DISK_ROOT "/dev"
+#define UDISK_ROOT "/dev/disk"
-#if defined(__sun__) || defined(__sun)
-#define DISK_ROOT "/dev/dsk"
-#define RDISK_ROOT "/dev/rdsk"
-#define UDISK_ROOT RDISK_ROOT
-#define FIRST_SLICE "s0"
-#define BACKUP_SLICE "s2"
-#endif
-
-#ifdef __linux__
-#define DISK_ROOT "/dev"
-#define RDISK_ROOT DISK_ROOT
-#define UDISK_ROOT "/dev/disk"
-#define FIRST_SLICE "1"
-#define BACKUP_SLICE ""
-#endif
+#define DEFAULT_IMPORT_PATH_SIZE 8
+extern char *zpool_default_import_path[DEFAULT_IMPORT_PATH_SIZE];
/*
* libzfs errors
@@ -658,8 +647,9 @@ extern zfs_handle_t *zfs_path_to_zhandle(libzfs_handle_t *, char *, zfs_type_t);
extern boolean_t zfs_dataset_exists(libzfs_handle_t *, const char *,
zfs_type_t);
extern int zfs_spa_version(zfs_handle_t *, int *);
-extern void zfs_append_partition(const char *path, char *buf, size_t buflen);
+extern int zfs_append_partition(char *path, size_t max_len);
extern int zfs_resolve_shortname(const char *name, char *path, size_t pathlen);
+extern int zfs_strcmp_pathname(char *name, char *cmp_name, int wholedisk);
/*
* Mount support functions.
diff --git a/lib/libzfs/libzfs_import.c b/lib/libzfs/libzfs_import.c
index b5cac6094..98dc8788b 100644
--- a/lib/libzfs/libzfs_import.c
+++ b/lib/libzfs/libzfs_import.c
@@ -984,9 +984,7 @@ err_blkid1:
}
#endif /* HAVE_LIBBLKID */
-#define DEFAULT_IMPORT_PATH_SIZE 8
-
-static char *
+char *
zpool_default_import_path[DEFAULT_IMPORT_PATH_SIZE] = {
"/dev/disk/by-vdev", /* Custom rules, use first if they exist */
"/dev/disk/zpool", /* Custom rules, use first if they exist */
diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c
index 2198a8285..75e883e4f 100644
--- a/lib/libzfs/libzfs_pool.c
+++ b/lib/libzfs/libzfs_pool.c
@@ -1744,10 +1744,11 @@ vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare,
/*
* Search for the requested value. Special cases:
*
- * - ZPOOL_CONFIG_PATH for whole disk entries. These end in with a
- * partition suffix "1", "-part1", or "p1". The suffix is hidden
- * from the user, but included in the string, so this matches around
- * it.
+ * - ZPOOL_CONFIG_PATH for whole disk entries. These end in
+ * "-part1", or "p1". The suffix is hidden from the user,
+ * but included in the string, so this matches around it.
+ * - ZPOOL_CONFIG_PATH for short names zfs_strcmp_shortname()
+ * is used to check all possible expanded paths.
* - looking for a top-level vdev name (i.e. ZPOOL_CONFIG_TYPE).
*
* Otherwise, all other searches are simple string compares.
@@ -1757,15 +1758,9 @@ vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare,
(void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
&wholedisk);
- if (wholedisk) {
- char buf[MAXPATHLEN];
-
- zfs_append_partition(srchval, buf, sizeof (buf));
- if (strcmp(val, buf) == 0)
- return (nv);
+ if (zfs_strcmp_pathname(srchval, val, wholedisk) == 0)
+ return (nv);
- break;
- }
} else if (strcmp(srchkey, ZPOOL_CONFIG_TYPE) == 0 && val) {
char *type, *idx, *end, *p;
uint64_t id, vdev_id;
@@ -1916,7 +1911,6 @@ nvlist_t *
zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare,
boolean_t *l2cache, boolean_t *log)
{
- char buf[MAXPATHLEN];
char *end;
nvlist_t *nvroot, *search, *ret;
uint64_t guid;
@@ -1928,12 +1922,6 @@ zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare,
verify(nvlist_add_uint64(search, ZPOOL_CONFIG_GUID, guid) == 0);
} else if (zpool_vdev_is_interior(path)) {
verify(nvlist_add_string(search, ZPOOL_CONFIG_TYPE, path) == 0);
- } else if (path[0] != '/') {
- if (zfs_resolve_shortname(path, buf, sizeof (buf)) < 0) {
- nvlist_free(search);
- return (NULL);
- }
- verify(nvlist_add_string(search, ZPOOL_CONFIG_PATH, buf) == 0);
} else {
verify(nvlist_add_string(search, ZPOOL_CONFIG_PATH, path) == 0);
}
@@ -3701,7 +3689,7 @@ read_efi_label(nvlist_t *config, diskaddr_t *sb)
if (nvlist_lookup_string(config, ZPOOL_CONFIG_PATH, &path) != 0)
return (err);
- (void) snprintf(diskname, sizeof (diskname), "%s%s", RDISK_ROOT,
+ (void) snprintf(diskname, sizeof (diskname), "%s%s", DISK_ROOT,
strrchr(path, '/'));
if ((fd = open(diskname, O_RDWR|O_DIRECT)) >= 0) {
struct dk_gpt *vtoc;
@@ -3839,8 +3827,7 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
start_block = NEW_START_BLOCK;
}
- (void) snprintf(path, sizeof (path), "%s/%s%s", RDISK_ROOT, name,
- BACKUP_SLICE);
+ (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name);
if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) {
/*
@@ -3910,9 +3897,11 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
(void) close(fd);
efi_free(vtoc);
- /* Wait for the first expected slice to appear. */
- (void) snprintf(path, sizeof (path), "%s/%s%s%s", DISK_ROOT, name,
- isdigit(name[strlen(name)-1]) ? "p" : "", FIRST_SLICE);
+ /* Wait for the first expected partition to appear. */
+
+ (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name);
+ (void) zfs_append_partition(path, MAXPATHLEN);
+
rval = zpool_label_disk_wait(path, 3000);
if (rval) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "failed to "
diff --git a/lib/libzfs/libzfs_util.c b/lib/libzfs/libzfs_util.c
index de4bb72de..16affd1ce 100644
--- a/lib/libzfs/libzfs_util.c
+++ b/lib/libzfs/libzfs_util.c
@@ -800,56 +800,165 @@ zfs_path_to_zhandle(libzfs_handle_t *hdl, char *path, zfs_type_t argtype)
}
/*
- * Given a shorthand device name, check if a file by that name exists in a list
- * of directories under /dev. If one is found, store its full path in the
- * buffer pointed to by the path argument and return 0, else return -1. The
- * path buffer must be allocated by the caller.
+ * Append partition suffix to an otherwise fully qualified device path.
+ * This is used to generate the name the full path as its stored in
+ * ZPOOL_CONFIG_PATH for whole disk devices. On success the new length
+ * of 'path' will be returned on error a negative value is returned.
*/
int
-zfs_resolve_shortname(const char *name, char *path, size_t pathlen)
+zfs_append_partition(char *path, size_t max_len)
{
- int i, err;
- char dirs[6][9] = {"by-id", "by-label", "by-path", "by-uuid", "zpool",
- "by-vdev"};
-
- /* /dev/ */
- (void) snprintf(path, pathlen, "%s/%s", DISK_ROOT, name);
- err = access(path, F_OK);
- if (err == 0)
- return (err);
-
- /* /dev/mapper/ */
- (void) snprintf(path, pathlen, "%s/mapper/%s", DISK_ROOT, name);
- err = access(path, F_OK);
- if (err == 0)
- return (err);
-
- /* /dev/disk/<dirs>/ */
- for (i = 0; i < 6 && err < 0; i++) {
- (void) snprintf(path, pathlen, "%s/%s/%s",
- UDISK_ROOT, dirs[i], name);
- err = access(path, F_OK);
+ int len = strlen(path);
+
+ if (strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0) {
+ if (len + 6 >= max_len)
+ return (-1);
+
+ (void) strcat(path, "-part1");
+ len += 6;
+ } else {
+ if (len + 2 >= max_len)
+ return (-1);
+
+ if (isdigit(path[len-1])) {
+ (void) strcat(path, "p1");
+ len += 2;
+ } else {
+ (void) strcat(path, "1");
+ len += 1;
+ }
}
- return (err);
+ return (len);
}
/*
- * Append partition suffix to a device path. This should be used to generate
- * the name of a whole disk as it is stored in the vdev label. The
- * user-visible names of whole disks do not contain the partition information.
- * Modifies buf which must be allocated by the caller.
+ * Given a shorthand device name check if a file by that name exists in any
+ * of the 'zpool_default_import_path' or ZPOOL_IMPORT_PATH directories. If
+ * one is found, store its fully qualified path in the 'path' buffer passed
+ * by the caller and return 0, otherwise return an error.
*/
-void
-zfs_append_partition(const char *path, char *buf, size_t buflen)
+int
+zfs_resolve_shortname(const char *name, char *path, size_t len)
{
- if (strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0)
- (void) snprintf(buf, buflen, "%s%s%s", path, "-part",
- FIRST_SLICE);
- else
- (void) snprintf(buf, buflen, "%s%s%s", path,
- isdigit(path[strlen(path)-1]) ? "p" : "",
- FIRST_SLICE);
+ int i, error = -1;
+ char *dir, *env, *envdup;
+
+ env = getenv("ZPOOL_IMPORT_PATH");
+ errno = ENOENT;
+
+ if (env) {
+ envdup = strdup(env);
+ dir = strtok(envdup, ":");
+ while (dir && error) {
+ (void) snprintf(path, len, "%s/%s", dir, name);
+ error = access(path, F_OK);
+ dir = strtok(NULL, ":");
+ }
+ free(envdup);
+ } else {
+ for (i = 0; i < DEFAULT_IMPORT_PATH_SIZE && error < 0; i++) {
+ (void) snprintf(path, len, "%s/%s",
+ zpool_default_import_path[i], name);
+ error = access(path, F_OK);
+ }
+ }
+
+ return (error ? ENOENT : 0);
+}
+
+/*
+ * Given a shorthand device name look for a match against 'cmp_name'. This
+ * is done by checking all prefix expansions using either the default
+ * 'zpool_default_import_paths' or the ZPOOL_IMPORT_PATH environment
+ * variable. Proper partition suffixes will be appended if this is a
+ * whole disk. When a match is found 0 is returned otherwise ENOENT.
+ */
+static int
+zfs_strcmp_shortname(char *name, char *cmp_name, int wholedisk)
+{
+ int path_len, cmp_len, i = 0, error = ENOENT;
+ char *dir, *env, *envdup = NULL;
+ char path_name[MAXPATHLEN];
+
+ cmp_len = strlen(cmp_name);
+ env = getenv("ZPOOL_IMPORT_PATH");
+
+ if (env) {
+ envdup = strdup(env);
+ dir = strtok(envdup, ":");
+ } else {
+ dir = zpool_default_import_path[i];
+ }
+
+ while (dir) {
+ /* Trim trailing directory slashes from ZPOOL_IMPORT_PATH */
+ while (dir[strlen(dir)-1] == '/')
+ dir[strlen(dir)-1] = '\0';
+
+ path_len = snprintf(path_name, MAXPATHLEN, "%s/%s", dir, name);
+ if (wholedisk)
+ path_len = zfs_append_partition(path_name, MAXPATHLEN);
+
+ if ((path_len == cmp_len) && !strcmp(path_name, cmp_name)) {
+ error = 0;
+ break;
+ }
+
+ if (env) {
+ dir = strtok(NULL, ":");
+ } else if (++i < DEFAULT_IMPORT_PATH_SIZE) {
+ dir = zpool_default_import_path[i];
+ } else {
+ dir = NULL;
+ }
+ }
+
+ if (env)
+ free(envdup);
+
+ return (error);
+}
+
+/*
+ * Given either a shorthand or fully qualified path name look for a match
+ * against 'cmp'. The passed name will be expanded as needed for comparison
+ * purposes and redundant slashes stripped to ensure an accurate match.
+ */
+int
+zfs_strcmp_pathname(char *name, char *cmp, int wholedisk)
+{
+ int path_len, cmp_len;
+ char path_name[MAXPATHLEN];
+ char cmp_name[MAXPATHLEN];
+ char *dir;
+
+ /* Strip redundant slashes if one exists due to ZPOOL_IMPORT_PATH */
+ memset(cmp_name, 0, MAXPATHLEN);
+ dir = strtok(cmp, "/");
+ while (dir) {
+ strcat(cmp_name, "/");
+ strcat(cmp_name, dir);
+ dir = strtok(NULL, "/");
+ }
+
+ if (name[0] != '/')
+ return zfs_strcmp_shortname(name, cmp_name, wholedisk);
+
+ strncpy(path_name, name, MAXPATHLEN);
+ path_len = strlen(path_name);
+ cmp_len = strlen(cmp_name);
+
+ if (wholedisk) {
+ path_len = zfs_append_partition(path_name, MAXPATHLEN);
+ if (path_len == -1)
+ return (ENOMEM);
+ }
+
+ if ((path_len != cmp_len) || strcmp(path_name, cmp_name))
+ return (ENOENT);
+
+ return (0);
}
/*