]> granicus.if.org Git - zfs/blobdiff - lib/libzfs/libzfs_util.c
OpenZFS 9166 - zfs storage pool checkpoint
[zfs] / lib / libzfs / libzfs_util.c
index d72b7c0fcdfb4bbaded5b77dd764fc9f05ea79e3..a19b34415dbd81cd3febea95ad8859b6c26f192a 100644 (file)
  *
  * CDDL HEADER END
  */
+
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2011, 2017 by Delphix. All rights reserved.
+ * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
+ * Copyright (c) 2017 Datto Inc.
  */
 
 /*
 #include <sys/mnttab.h>
 #include <sys/mntent.h>
 #include <sys/types.h>
-#include <wait.h>
+#include <sys/wait.h>
 
 #include <libzfs.h>
+#include <libzfs_core.h>
 
 #include "libzfs_impl.h"
 #include "zfs_prop.h"
+#include "zfeature_common.h"
+#include <zfs_fletcher.h>
 
 int
 libzfs_errno(libzfs_handle_t *hdl)
@@ -53,6 +61,31 @@ libzfs_errno(libzfs_handle_t *hdl)
        return (hdl->libzfs_error);
 }
 
+const char *
+libzfs_error_init(int error)
+{
+       switch (error) {
+       case ENXIO:
+               return (dgettext(TEXT_DOMAIN, "The ZFS modules are not "
+                   "loaded.\nTry running '/sbin/modprobe zfs' as root "
+                   "to load them.\n"));
+       case ENOENT:
+               return (dgettext(TEXT_DOMAIN, "/dev/zfs and /proc/self/mounts "
+                   "are required.\nTry running 'udevadm trigger' and 'mount "
+                   "-t proc proc /proc' as root.\n"));
+       case ENOEXEC:
+               return (dgettext(TEXT_DOMAIN, "The ZFS modules cannot be "
+                   "auto-loaded.\nTry running '/sbin/modprobe zfs' as "
+                   "root to manually load them.\n"));
+       case EACCES:
+               return (dgettext(TEXT_DOMAIN, "Permission denied the "
+                   "ZFS utilities must be run as root.\n"));
+       default:
+               return (dgettext(TEXT_DOMAIN, "Failed to initialize the "
+                   "libzfs library.\n"));
+       }
+}
+
 const char *
 libzfs_error_action(libzfs_handle_t *hdl)
 {
@@ -113,7 +146,8 @@ libzfs_error_description(libzfs_handle_t *hdl)
        case EZFS_RESILVERING:
                return (dgettext(TEXT_DOMAIN, "currently resilvering"));
        case EZFS_BADVERSION:
-               return (dgettext(TEXT_DOMAIN, "unsupported version"));
+               return (dgettext(TEXT_DOMAIN, "unsupported version or "
+                   "feature"));
        case EZFS_POOLUNAVAIL:
                return (dgettext(TEXT_DOMAIN, "pool is unavailable"));
        case EZFS_DEVOVERFLOW:
@@ -213,6 +247,9 @@ libzfs_error_description(libzfs_handle_t *hdl)
        case EZFS_POSTSPLIT_ONLINE:
                return (dgettext(TEXT_DOMAIN, "disk was split from this pool "
                    "into a new one"));
+       case EZFS_SCRUB_PAUSED:
+               return (dgettext(TEXT_DOMAIN, "scrub is paused; "
+                   "use 'zpool scrub' to resume"));
        case EZFS_SCRUBBING:
                return (dgettext(TEXT_DOMAIN, "currently scrubbing; "
                    "use 'zpool scrub -s' to cancel current scrub"));
@@ -224,6 +261,25 @@ libzfs_error_description(libzfs_handle_t *hdl)
                return (dgettext(TEXT_DOMAIN, "invalid diff data"));
        case EZFS_POOLREADONLY:
                return (dgettext(TEXT_DOMAIN, "pool is read-only"));
+       case EZFS_NO_PENDING:
+               return (dgettext(TEXT_DOMAIN, "operation is not "
+                   "in progress"));
+       case EZFS_CHECKPOINT_EXISTS:
+               return (dgettext(TEXT_DOMAIN, "checkpoint exists"));
+       case EZFS_DISCARDING_CHECKPOINT:
+               return (dgettext(TEXT_DOMAIN, "currently discarding "
+                   "checkpoint"));
+       case EZFS_NO_CHECKPOINT:
+               return (dgettext(TEXT_DOMAIN, "checkpoint does not exist"));
+       case EZFS_DEVRM_IN_PROGRESS:
+               return (dgettext(TEXT_DOMAIN, "device removal in progress"));
+       case EZFS_VDEV_TOO_BIG:
+               return (dgettext(TEXT_DOMAIN, "device exceeds supported size"));
+       case EZFS_ACTIVE_POOL:
+               return (dgettext(TEXT_DOMAIN, "pool is imported on a "
+                   "different host"));
+       case EZFS_CRYPTOFAILED:
+               return (dgettext(TEXT_DOMAIN, "encryption failure"));
        case EZFS_UNKNOWN:
                return (dgettext(TEXT_DOMAIN, "unknown error"));
        default:
@@ -346,6 +402,7 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
        switch (error) {
        case ENXIO:
        case ENODEV:
+       case EPIPE:
                zfs_verror(hdl, EZFS_IO, fmt, ap);
                break;
 
@@ -358,7 +415,7 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
        case ENOSPC:
        case EDQUOT:
                zfs_verror(hdl, EZFS_NOSPC, fmt, ap);
-               return (-1);
+               break;
 
        case EEXIST:
                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -385,6 +442,9 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
                    "pool I/O is currently suspended"));
                zfs_verror(hdl, EZFS_POOLUNAVAIL, fmt, ap);
                break;
+       case EREMOTEIO:
+               zfs_verror(hdl, EZFS_ACTIVE_POOL, fmt, ap);
+               break;
        default:
                zfs_error_aux(hdl, strerror(error));
                zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
@@ -436,6 +496,11 @@ zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
                zfs_verror(hdl, EZFS_BUSY, fmt, ap);
                break;
 
+       /* There is no pending operation to cancel */
+       case ENOTACTIVE:
+               zfs_verror(hdl, EZFS_NO_PENDING, fmt, ap);
+               break;
+
        case ENXIO:
                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                    "one or more devices is currently unavailable"));
@@ -468,7 +533,29 @@ zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
        case EROFS:
                zfs_verror(hdl, EZFS_POOLREADONLY, fmt, ap);
                break;
-
+       case EDOM:
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                   "block size out of range or does not match"));
+               zfs_verror(hdl, EZFS_BADPROP, fmt, ap);
+               break;
+       case EREMOTEIO:
+               zfs_verror(hdl, EZFS_ACTIVE_POOL, fmt, ap);
+               break;
+       case ZFS_ERR_CHECKPOINT_EXISTS:
+               zfs_verror(hdl, EZFS_CHECKPOINT_EXISTS, fmt, ap);
+               break;
+       case ZFS_ERR_DISCARDING_CHECKPOINT:
+               zfs_verror(hdl, EZFS_DISCARDING_CHECKPOINT, fmt, ap);
+               break;
+       case ZFS_ERR_NO_CHECKPOINT:
+               zfs_verror(hdl, EZFS_NO_CHECKPOINT, fmt, ap);
+               break;
+       case ZFS_ERR_DEVRM_IN_PROGRESS:
+               zfs_verror(hdl, EZFS_DEVRM_IN_PROGRESS, fmt, ap);
+               break;
+       case ZFS_ERR_VDEV_TOO_BIG:
+               zfs_verror(hdl, EZFS_VDEV_TOO_BIG, fmt, ap);
+               break;
        default:
                zfs_error_aux(hdl, strerror(error));
                zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
@@ -559,27 +646,57 @@ zfs_strdup(libzfs_handle_t *hdl, const char *str)
  * Convert a number to an appropriately human-readable output.
  */
 void
-zfs_nicenum(uint64_t num, char *buf, size_t buflen)
+zfs_nicenum_format(uint64_t num, char *buf, size_t buflen,
+    enum zfs_nicenum_format format)
 {
        uint64_t n = num;
        int index = 0;
-       char u;
+       const char *u;
+       const char *units[3][7] = {
+           [ZFS_NICENUM_1024] = {"", "K", "M", "G", "T", "P", "E"},
+           [ZFS_NICENUM_BYTES] = {"B", "K", "M", "G", "T", "P", "E"},
+           [ZFS_NICENUM_TIME] = {"ns", "us", "ms", "s", "?", "?", "?"}
+       };
+
+       const int units_len[] = {[ZFS_NICENUM_1024] = 6,
+           [ZFS_NICENUM_BYTES] = 6,
+           [ZFS_NICENUM_TIME] = 4};
+
+       const int k_unit[] = {  [ZFS_NICENUM_1024] = 1024,
+           [ZFS_NICENUM_BYTES] = 1024,
+           [ZFS_NICENUM_TIME] = 1000};
+
+       double val;
+
+       if (format == ZFS_NICENUM_RAW) {
+               snprintf(buf, buflen, "%llu", (u_longlong_t)num);
+               return;
+       } else if (format == ZFS_NICENUM_RAWTIME && num > 0) {
+               snprintf(buf, buflen, "%llu", (u_longlong_t)num);
+               return;
+       } else if (format == ZFS_NICENUM_RAWTIME && num == 0) {
+               snprintf(buf, buflen, "%s", "-");
+               return;
+       }
 
-       while (n >= 1024) {
-               n /= 1024;
+       while (n >= k_unit[format] && index < units_len[format]) {
+               n /= k_unit[format];
                index++;
        }
 
-       u = " KMGTPE"[index];
+       u = units[format][index];
 
-       if (index == 0) {
-               (void) snprintf(buf, buflen, "%llu", (u_longlong_t) n);
-       } else if ((num & ((1ULL << 10 * index) - 1)) == 0) {
+       /* Don't print zero latencies since they're invalid */
+       if ((format == ZFS_NICENUM_TIME) && (num == 0)) {
+               (void) snprintf(buf, buflen, "-");
+       } else if ((index == 0) || ((num %
+           (uint64_t)powl(k_unit[format], index)) == 0)) {
                /*
                 * If this is an even multiple of the base, always display
                 * without any decimal precision.
                 */
-               (void) snprintf(buf, buflen, "%llu%c", (u_longlong_t) n, u);
+               (void) snprintf(buf, buflen, "%llu%s", (u_longlong_t)n, u);
+
        } else {
                /*
                 * We want to choose a precision that reflects the best choice
@@ -592,13 +709,68 @@ zfs_nicenum(uint64_t num, char *buf, size_t buflen)
                 */
                int i;
                for (i = 2; i >= 0; i--) {
-                       if (snprintf(buf, buflen, "%.*f%c", i,
-                           (double)num / (1ULL << 10 * index), u) <= 5)
-                               break;
+                       val = (double)num /
+                           (uint64_t)powl(k_unit[format], index);
+
+                       /*
+                        * Don't print floating point values for time.  Note,
+                        * we use floor() instead of round() here, since
+                        * round can result in undesirable results.  For
+                        * example, if "num" is in the range of
+                        * 999500-999999, it will print out "1000us".  This
+                        * doesn't happen if we use floor().
+                        */
+                       if (format == ZFS_NICENUM_TIME) {
+                               if (snprintf(buf, buflen, "%d%s",
+                                   (unsigned int) floor(val), u) <= 5)
+                                       break;
+
+                       } else {
+                               if (snprintf(buf, buflen, "%.*f%s", i,
+                                   val, u) <= 5)
+                                       break;
+                       }
                }
        }
 }
 
+/*
+ * Convert a number to an appropriately human-readable output.
+ */
+void
+zfs_nicenum(uint64_t num, char *buf, size_t buflen)
+{
+       zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_1024);
+}
+
+/*
+ * Convert a time to an appropriately human-readable output.
+ * @num:       Time in nanoseconds
+ */
+void
+zfs_nicetime(uint64_t num, char *buf, size_t buflen)
+{
+       zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_TIME);
+}
+
+/*
+ * Print out a raw number with correct column spacing
+ */
+void
+zfs_niceraw(uint64_t num, char *buf, size_t buflen)
+{
+       zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_RAW);
+}
+
+/*
+ * Convert a number of bytes to an appropriately human-readable output.
+ */
+void
+zfs_nicebytes(uint64_t num, char *buf, size_t buflen)
+{
+       zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_BYTES);
+}
+
 void
 libzfs_print_on_error(libzfs_handle_t *hdl, boolean_t printerr)
 {
@@ -608,74 +780,274 @@ libzfs_print_on_error(libzfs_handle_t *hdl, boolean_t printerr)
 static int
 libzfs_module_loaded(const char *module)
 {
-       FILE *f;
-       int result = 0;
-       char name[256];
+       const char path_prefix[] = "/sys/module/";
+       char path[256];
 
-       f = fopen("/proc/modules", "r");
-       if (f == NULL)
-               return -1;
+       memcpy(path, path_prefix, sizeof (path_prefix) - 1);
+       strcpy(path + sizeof (path_prefix) - 1, module);
 
-       while (fgets(name, sizeof(name), f)) {
-               char *c = strchr(name, ' ');
-               if (!c)
-                       continue;
-               *c = 0;
-               if (strcmp(module, name) == 0) {
-                       result = 1;
+       return (access(path, F_OK) == 0);
+}
+
+
+/*
+ * Read lines from an open file descriptor and store them in an array of
+ * strings until EOF.  lines[] will be allocated and populated with all the
+ * lines read.  All newlines are replaced with NULL terminators for
+ * convenience.  lines[] must be freed after use with libzfs_free_str_array().
+ *
+ * Returns the number of lines read.
+ */
+static int
+libzfs_read_stdout_from_fd(int fd, char **lines[])
+{
+
+       FILE *fp;
+       int lines_cnt = 0;
+       size_t len = 0;
+       char *line = NULL;
+       char **tmp_lines = NULL, **tmp;
+       char *nl = NULL;
+       int rc;
+
+       fp = fdopen(fd, "r");
+       if (fp == NULL)
+               return (0);
+       while (1) {
+               rc = getline(&line, &len, fp);
+               if (rc == -1)
+                       break;
+
+               tmp = realloc(tmp_lines, sizeof (*tmp_lines) * (lines_cnt + 1));
+               if (tmp == NULL) {
+                       /* Return the lines we were able to process */
                        break;
                }
+               tmp_lines = tmp;
+
+               /* Terminate newlines */
+               if ((nl = strchr(line, '\n')) != NULL)
+                       *nl = '\0';
+               tmp_lines[lines_cnt] = line;
+               lines_cnt++;
+               line = NULL;
        }
-       fclose(f);
-
-       return result;
+       fclose(fp);
+       *lines = tmp_lines;
+       return (lines_cnt);
 }
 
 static int
-libzfs_run_process(const char *path, char *argv[])
+libzfs_run_process_impl(const char *path, char *argv[], char *env[], int flags,
+    char **lines[], int *lines_cnt)
 {
        pid_t pid;
-       int rc;
+       int error, devnull_fd;
+       int link[2];
+
+       /*
+        * Setup a pipe between our child and parent process if we're
+        * reading stdout.
+        */
+       if ((lines != NULL) && pipe(link) == -1)
+               return (-ESTRPIPE);
 
        pid = vfork();
        if (pid == 0) {
-               close(1);
-               close(2);
-               (void) execvp(path, argv);
+               /* Child process */
+               devnull_fd = open("/dev/null", O_WRONLY);
+
+               if (devnull_fd < 0)
+                       _exit(-1);
+
+               if (!(flags & STDOUT_VERBOSE) && (lines == NULL))
+                       (void) dup2(devnull_fd, STDOUT_FILENO);
+               else if (lines != NULL) {
+                       /* Save the output to lines[] */
+                       dup2(link[1], STDOUT_FILENO);
+                       close(link[0]);
+                       close(link[1]);
+               }
+
+               if (!(flags & STDERR_VERBOSE))
+                       (void) dup2(devnull_fd, STDERR_FILENO);
+
+               close(devnull_fd);
+
+               if (flags & NO_DEFAULT_PATH) {
+                       if (env == NULL)
+                               execv(path, argv);
+                       else
+                               execve(path, argv, env);
+               } else {
+                       if (env == NULL)
+                               execvp(path, argv);
+                       else
+                               execvpe(path, argv, env);
+               }
+
                _exit(-1);
        } else if (pid > 0) {
+               /* Parent process */
                int status;
 
-               while ((rc = waitpid(pid, &status, 0)) == -1 &&
-                       errno == EINTR);
-               if (rc < 0 || !WIFEXITED(status))
-                       return -1;
+               while ((error = waitpid(pid, &status, 0)) == -1 &&
+                   errno == EINTR) { }
+               if (error < 0 || !WIFEXITED(status))
+                       return (-1);
 
-               return WEXITSTATUS(status);
+               if (lines != NULL) {
+                       close(link[1]);
+                       *lines_cnt = libzfs_read_stdout_from_fd(link[0], lines);
+               }
+               return (WEXITSTATUS(status));
        }
 
-       return -1;
+       return (-1);
+}
+
+int
+libzfs_run_process(const char *path, char *argv[], int flags)
+{
+       return (libzfs_run_process_impl(path, argv, NULL, flags, NULL, NULL));
+}
+
+/*
+ * Run a command and store its stdout lines in an array of strings (lines[]).
+ * lines[] is allocated and populated for you, and the number of lines is set in
+ * lines_cnt.  lines[] must be freed after use with libzfs_free_str_array().
+ * All newlines (\n) in lines[] are terminated for convenience.
+ */
+int
+libzfs_run_process_get_stdout(const char *path, char *argv[], char *env[],
+    char **lines[], int *lines_cnt)
+{
+       return (libzfs_run_process_impl(path, argv, env, 0, lines, lines_cnt));
 }
 
+/*
+ * Same as libzfs_run_process_get_stdout(), but run without $PATH set.  This
+ * means that *path needs to be the full path to the executable.
+ */
+int
+libzfs_run_process_get_stdout_nopath(const char *path, char *argv[],
+    char *env[], char **lines[], int *lines_cnt)
+{
+       return (libzfs_run_process_impl(path, argv, env, NO_DEFAULT_PATH,
+           lines, lines_cnt));
+}
+
+/*
+ * Free an array of strings.  Free both the strings contained in the array and
+ * the array itself.
+ */
+void
+libzfs_free_str_array(char **strs, int count)
+{
+       while (--count >= 0)
+               free(strs[count]);
+
+       free(strs);
+}
+
+/*
+ * Returns 1 if environment variable is set to "YES", "yes", "ON", "on", or
+ * a non-zero number.
+ *
+ * Returns 0 otherwise.
+ */
+int
+libzfs_envvar_is_set(char *envvar)
+{
+       char *env = getenv(envvar);
+       if (env && (strtoul(env, NULL, 0) > 0 ||
+           (!strncasecmp(env, "YES", 3) && strnlen(env, 4) == 3) ||
+           (!strncasecmp(env, "ON", 2) && strnlen(env, 3) == 2)))
+               return (1);
+
+       return (0);
+}
+
+/*
+ * Verify the required ZFS_DEV device is available and optionally attempt
+ * to load the ZFS modules.  Under normal circumstances the modules
+ * should already have been loaded by some external mechanism.
+ *
+ * Environment variables:
+ * - ZFS_MODULE_LOADING="YES|yes|ON|on" - Attempt to load modules.
+ * - ZFS_MODULE_TIMEOUT="<seconds>"     - Seconds to wait for ZFS_DEV
+ */
 static int
 libzfs_load_module(const char *module)
 {
        char *argv[4] = {"/sbin/modprobe", "-q", (char *)module, (char *)0};
+       char *load_str, *timeout_str;
+       long timeout = 10; /* seconds */
+       long busy_timeout = 10; /* milliseconds */
+       int load = 0, fd;
+       hrtime_t start;
+
+       /* Optionally request module loading */
+       if (!libzfs_module_loaded(module)) {
+               load_str = getenv("ZFS_MODULE_LOADING");
+               if (load_str) {
+                       if (!strncasecmp(load_str, "YES", strlen("YES")) ||
+                           !strncasecmp(load_str, "ON", strlen("ON")))
+                               load = 1;
+                       else
+                               load = 0;
+               }
 
-       if (libzfs_module_loaded(module))
-               return 0;
-       return libzfs_run_process("modprobe", argv);
+               if (load) {
+                       if (libzfs_run_process("/sbin/modprobe", argv, 0))
+                               return (ENOEXEC);
+
+                       if (!libzfs_module_loaded(module))
+                               return (ENXIO);
+               }
+       }
+
+       /*
+        * Device creation by udev is asynchronous and waiting may be
+        * required.  Busy wait for 10ms and then fall back to polling every
+        * 10ms for the allowed timeout (default 10s, max 10m).  This is
+        * done to optimize for the common case where the device is
+        * immediately available and to avoid penalizing the possible
+        * case where udev is slow or unable to create the device.
+        */
+       timeout_str = getenv("ZFS_MODULE_TIMEOUT");
+       if (timeout_str) {
+               timeout = strtol(timeout_str, NULL, 0);
+               timeout = MAX(MIN(timeout, (10 * 60)), 0); /* 0 <= N <= 600 */
+       }
+
+       start = gethrtime();
+       do {
+               fd = open(ZFS_DEV, O_RDWR);
+               if (fd >= 0) {
+                       (void) close(fd);
+                       return (0);
+               } else if (errno != ENOENT) {
+                       return (errno);
+               } else if (NSEC2MSEC(gethrtime() - start) < busy_timeout) {
+                       sched_yield();
+               } else {
+                       usleep(10 * MILLISEC);
+               }
+       } while (NSEC2MSEC(gethrtime() - start) < (timeout * MILLISEC));
+
+       return (ENOENT);
 }
 
 libzfs_handle_t *
 libzfs_init(void)
 {
        libzfs_handle_t *hdl;
+       int error;
 
-       if (libzfs_load_module("zfs") != 0) {
-               (void) fprintf(stderr, gettext("Failed to load ZFS module "
-                              "stack.\nLoad the module manually by running "
-                              "'insmod <location>/zfs.ko' as root.\n"));
+       error = libzfs_load_module(ZFS_DRIVER);
+       if (error) {
+               errno = error;
                return (NULL);
        }
 
@@ -684,13 +1056,6 @@ libzfs_init(void)
        }
 
        if ((hdl->libzfs_fd = open(ZFS_DEV, O_RDWR)) < 0) {
-               (void) fprintf(stderr, gettext("Unable to open %s: %s.\n"),
-                              ZFS_DEV, strerror(errno));
-               if (errno == ENOENT)
-                       (void) fprintf(stderr,
-                            gettext("Verify the ZFS module stack is "
-                            "loaded by running '/sbin/modprobe zfs'.\n"));
-
                free(hdl);
                return (NULL);
        }
@@ -705,11 +1070,26 @@ libzfs_init(void)
                return (NULL);
        }
 
-       hdl->libzfs_sharetab = fopen("/etc/dfs/sharetab", "r");
+       hdl->libzfs_sharetab = fopen(ZFS_SHARETAB, "r");
+
+       if (libzfs_core_init() != 0) {
+               (void) close(hdl->libzfs_fd);
+               (void) fclose(hdl->libzfs_mnttab);
+               if (hdl->libzfs_sharetab)
+                       (void) fclose(hdl->libzfs_sharetab);
+               free(hdl);
+               return (NULL);
+       }
 
        zfs_prop_init();
        zpool_prop_init();
+       zpool_feature_init();
        libzfs_mnttab_init(hdl);
+       fletcher_4_init();
+
+       if (getenv("ZFS_PROP_DEBUG") != NULL) {
+               hdl->libzfs_prop_debug = B_TRUE;
+       }
 
        return (hdl);
 }
@@ -726,15 +1106,12 @@ libzfs_fini(libzfs_handle_t *hdl)
 #endif
        if (hdl->libzfs_sharetab)
                (void) fclose(hdl->libzfs_sharetab);
-#ifdef HAVE_ZPL
        zfs_uninit_libshare(hdl);
-#endif
-       if (hdl->libzfs_log_str)
-               (void) free(hdl->libzfs_log_str);
        zpool_free_handles(hdl);
-       libzfs_fru_clear(hdl, B_TRUE);
        namespace_clear(hdl);
        libzfs_mnttab_fini(hdl);
+       libzfs_core_fini();
+       fletcher_4_fini();
        free(hdl);
 }
 
@@ -760,7 +1137,7 @@ zfs_get_pool_handle(const zfs_handle_t *zhp)
  * Given a name, determine whether or not it's a valid path
  * (starts with '/' or "./").  If so, walk the mnttab trying
  * to match the device number.  If not, treat the path as an
- * fs/vol/snap name.
+ * fs/vol/snap/bkmark name.
  */
 zfs_handle_t *
 zfs_path_to_zhandle(libzfs_handle_t *hdl, char *path, zfs_type_t argtype)
@@ -781,7 +1158,10 @@ zfs_path_to_zhandle(libzfs_handle_t *hdl, char *path, zfs_type_t argtype)
                return (NULL);
        }
 
-       rewind(hdl->libzfs_mnttab);
+       /* Reopen MNTTAB to prevent reading stale data from open file */
+       if (freopen(MNTTAB, "r", hdl->libzfs_mnttab) == NULL)
+               return (NULL);
+
        while ((ret = getextmntent(hdl->libzfs_mnttab, &entry, 0)) == 0) {
                if (makedevice(entry.mnt_major, entry.mnt_minor) ==
                    statbuf.st_dev) {
@@ -801,6 +1181,210 @@ zfs_path_to_zhandle(libzfs_handle_t *hdl, char *path, zfs_type_t argtype)
        return (zfs_open(hdl, entry.mnt_special, ZFS_TYPE_FILESYSTEM));
 }
 
+/*
+ * 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_append_partition(char *path, size_t max_len)
+{
+       int len = strlen(path);
+
+       if ((strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0) ||
+           (strncmp(path, ZVOL_ROOT, strlen(ZVOL_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 (len);
+}
+
+/*
+ * 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.
+ */
+int
+zfs_resolve_shortname(const char *name, char *path, size_t len)
+{
+       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) == 0) {
+                       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, *dup;
+
+       /* Strip redundant slashes if one exists due to ZPOOL_IMPORT_PATH */
+       memset(cmp_name, 0, MAXPATHLEN);
+       dup = strdup(cmp);
+       dir = strtok(dup, "/");
+       while (dir) {
+               strlcat(cmp_name, "/", sizeof (cmp_name));
+               strlcat(cmp_name, dir, sizeof (cmp_name));
+               dir = strtok(NULL, "/");
+       }
+       free(dup);
+
+       if (name[0] != '/')
+               return (zfs_strcmp_shortname(name, cmp_name, wholedisk));
+
+       (void) strlcpy(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);
+}
+
+/*
+ * Given a full path to a device determine if that device appears in the
+ * import search path.  If it does return the first match and store the
+ * index in the passed 'order' variable, otherwise return an error.
+ */
+int
+zfs_path_order(char *name, int *order)
+{
+       int i = 0, error = ENOENT;
+       char *dir, *env, *envdup;
+
+       env = getenv("ZPOOL_IMPORT_PATH");
+       if (env) {
+               envdup = strdup(env);
+               dir = strtok(envdup, ":");
+               while (dir) {
+                       if (strncmp(name, dir, strlen(dir)) == 0) {
+                               *order = i;
+                               error = 0;
+                               break;
+                       }
+                       dir = strtok(NULL, ":");
+                       i++;
+               }
+               free(envdup);
+       } else {
+               for (i = 0; i < DEFAULT_IMPORT_PATH_SIZE; i++) {
+                       if (strncmp(name, zpool_default_import_path[i],
+                           strlen(zpool_default_import_path[i])) == 0) {
+                               *order = i;
+                               error = 0;
+                               break;
+                       }
+               }
+       }
+
+       return (error);
+}
+
 /*
  * Initialize the zc_nvlist_dst member to prepare for receiving an nvlist from
  * an ioctl().
@@ -811,8 +1395,9 @@ zcmd_alloc_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, size_t len)
        if (len == 0)
                len = 16 * 1024;
        zc->zc_nvlist_dst_size = len;
-       if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t)
-           zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == 0)
+       zc->zc_nvlist_dst =
+           (uint64_t)(uintptr_t)zfs_alloc(hdl, zc->zc_nvlist_dst_size);
+       if (zc->zc_nvlist_dst == 0)
                return (-1);
 
        return (0);
@@ -827,8 +1412,9 @@ int
 zcmd_expand_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc)
 {
        free((void *)(uintptr_t)zc->zc_nvlist_dst);
-       if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t)
-           zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == 0)
+       zc->zc_nvlist_dst =
+           (uint64_t)(uintptr_t)zfs_alloc(hdl, zc->zc_nvlist_dst_size);
+       if (zc->zc_nvlist_dst == 0)
                return (-1);
 
        return (0);
@@ -843,6 +1429,9 @@ zcmd_free_nvlists(zfs_cmd_t *zc)
        free((void *)(uintptr_t)zc->zc_nvlist_conf);
        free((void *)(uintptr_t)zc->zc_nvlist_src);
        free((void *)(uintptr_t)zc->zc_nvlist_dst);
+       zc->zc_nvlist_conf = 0;
+       zc->zc_nvlist_src = 0;
+       zc->zc_nvlist_dst = 0;
 }
 
 static int
@@ -895,17 +1484,7 @@ zcmd_read_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t **nvlp)
 int
 zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
 {
-       int error;
-
-       zc->zc_history = (uint64_t)(uintptr_t)hdl->libzfs_log_str;
-       error = ioctl(hdl->libzfs_fd, request, zc);
-       if (hdl->libzfs_log_str) {
-               free(hdl->libzfs_log_str);
-               hdl->libzfs_log_str = NULL;
-       }
-       zc->zc_history = 0;
-
-       return (error);
+       return (ioctl(hdl->libzfs_fd, request, zc));
 }
 
 /*
@@ -1096,6 +1675,10 @@ zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp,
                        case ZPROP_SRC_RECEIVED:
                                str = "received";
                                break;
+
+                       default:
+                               str = NULL;
+                               assert(!"unhandled zprop_source_t");
                        }
                        break;
 
@@ -1107,7 +1690,8 @@ zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp,
                        continue;
                }
 
-               if (cbp->cb_columns[i + 1] == GET_COL_NONE)
+               if (i == (ZFS_GET_NCOLS - 1) ||
+                   cbp->cb_columns[i + 1] == GET_COL_NONE)
                        (void) printf("%s", str);
                else if (cbp->cb_scripted)
                        (void) printf("%s\t", str);
@@ -1137,21 +1721,26 @@ str2shift(libzfs_handle_t *hdl, const char *buf)
                        break;
        }
        if (i == strlen(ends)) {
-               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                   "invalid numeric suffix '%s'"), buf);
+               if (hdl)
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "invalid numeric suffix '%s'"), buf);
                return (-1);
        }
 
        /*
-        * We want to allow trailing 'b' characters for 'GB' or 'Mb'.  But don't
-        * allow 'BB' - that's just weird.
+        * Allow 'G' = 'GB' = 'GiB', case-insensitively.
+        * However, 'BB' and 'BiB' are disallowed.
         */
-       if (buf[1] == '\0' || (toupper(buf[1]) == 'B' && buf[2] == '\0' &&
-           toupper(buf[0]) != 'B'))
-               return (10*i);
-
-       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-           "invalid numeric suffix '%s'"), buf);
+       if (buf[1] == '\0' ||
+           (toupper(buf[0]) != 'B' &&
+           ((toupper(buf[1]) == 'B' && buf[2] == '\0') ||
+           (toupper(buf[1]) == 'I' && toupper(buf[2]) == 'B' &&
+           buf[3] == '\0'))))
+               return (10 * i);
+
+       if (hdl)
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                   "invalid numeric suffix '%s'"), buf);
        return (-1);
 }
 
@@ -1250,6 +1839,8 @@ zprop_parse_value(libzfs_handle_t *hdl, nvpair_t *elem, int prop,
        const char *propname;
        char *value;
        boolean_t isnone = B_FALSE;
+       boolean_t isauto = B_FALSE;
+       int err = 0;
 
        if (type == ZFS_TYPE_POOL) {
                proptype = zpool_prop_get_type(prop);
@@ -1272,7 +1863,12 @@ zprop_parse_value(libzfs_handle_t *hdl, nvpair_t *elem, int prop,
                            "'%s' must be a string"), nvpair_name(elem));
                        goto error;
                }
-               (void) nvpair_value_string(elem, svalp);
+               err = nvpair_value_string(elem, svalp);
+               if (err != 0) {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "'%s' is invalid"), nvpair_name(elem));
+                       goto error;
+               }
                if (strlen(*svalp) >= ZFS_MAXPROPLEN) {
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                            "'%s' is too long"), nvpair_name(elem));
@@ -1285,8 +1881,9 @@ zprop_parse_value(libzfs_handle_t *hdl, nvpair_t *elem, int prop,
                        (void) nvpair_value_string(elem, &value);
                        if (strcmp(value, "none") == 0) {
                                isnone = B_TRUE;
-                       } else if (zfs_nicestrtonum(hdl, value, ivalp)
-                           != 0) {
+                       } else if (strcmp(value, "auto") == 0) {
+                               isauto = B_TRUE;
+                       } else if (zfs_nicestrtonum(hdl, value, ivalp) != 0) {
                                goto error;
                        }
                } else if (datatype == DATA_TYPE_UINT64) {
@@ -1306,6 +1903,41 @@ zprop_parse_value(libzfs_handle_t *hdl, nvpair_t *elem, int prop,
                            "use 'none' to disable quota/refquota"));
                        goto error;
                }
+
+               /*
+                * Special handling for "*_limit=none". In this case it's not
+                * 0 but UINT64_MAX.
+                */
+               if ((type & ZFS_TYPE_DATASET) && isnone &&
+                   (prop == ZFS_PROP_FILESYSTEM_LIMIT ||
+                   prop == ZFS_PROP_SNAPSHOT_LIMIT)) {
+                       *ivalp = UINT64_MAX;
+               }
+
+               /*
+                * Special handling for setting 'refreservation' to 'auto'.  Use
+                * UINT64_MAX to tell the caller to use zfs_fix_auto_resv().
+                * 'auto' is only allowed on volumes.
+                */
+               if (isauto) {
+                       switch (prop) {
+                       case ZFS_PROP_REFRESERVATION:
+                               if ((type & ZFS_TYPE_VOLUME) == 0) {
+                                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                           "'%s=auto' only allowed on "
+                                           "volumes"), nvpair_name(elem));
+                                       goto error;
+                               }
+                               *ivalp = UINT64_MAX;
+                               break;
+                       default:
+                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                   "'auto' is invalid value for '%s'"),
+                                   nvpair_name(elem));
+                               goto error;
+                       }
+               }
+
                break;
 
        case PROP_TYPE_INDEX:
@@ -1359,7 +1991,7 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
 
        prop = zprop_name_to_prop(propname, type);
 
-       if (prop != ZPROP_INVAL && !zprop_valid_for_type(prop, type))
+       if (prop != ZPROP_INVAL && !zprop_valid_for_type(prop, type, B_FALSE))
                prop = ZPROP_INVAL;
 
        /*
@@ -1367,8 +1999,11 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
         * this is a pool property or if this isn't a user-defined
         * dataset property,
         */
-       if (prop == ZPROP_INVAL && (type == ZFS_TYPE_POOL ||
-           (!zfs_prop_user(propname) && !zfs_prop_userquota(propname)))) {
+       if (prop == ZPROP_INVAL && ((type == ZFS_TYPE_POOL &&
+           !zpool_prop_feature(propname) &&
+           !zpool_prop_unsupported(propname)) ||
+           (type == ZFS_TYPE_DATASET && !zfs_prop_user(propname) &&
+           !zfs_prop_userquota(propname) && !zfs_prop_written(propname)))) {
                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                    "invalid property '%s'"), propname);
                return (zfs_error(hdl, EZFS_BADPROP,
@@ -1380,7 +2015,8 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
 
        entry->pl_prop = prop;
        if (prop == ZPROP_INVAL) {
-               if ((entry->pl_user_prop = zfs_strdup(hdl, propname)) == NULL) {
+               if ((entry->pl_user_prop = zfs_strdup(hdl, propname)) ==
+                   NULL) {
                        free(entry);
                        return (-1);
                }