]> git.proxmox.com Git - mirror_zfs-debian.git/blobdiff - lib/libzfs/libzfs_util.c
Merge tag 'upstream/0.6.5.8'
[mirror_zfs-debian.git] / lib / libzfs / libzfs_util.c
old mode 100644 (file)
new mode 100755 (executable)
index a4e1255..56dfd95
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, Joyent, Inc. All rights reserved.
  * Copyright (c) 2012 by Delphix. All rights reserved.
  */
 
@@ -45,6 +46,7 @@
 #include <wait.h>
 
 #include <libzfs.h>
+#include <libzfs_core.h>
 
 #include "libzfs_impl.h"
 #include "zfs_prop.h"
@@ -56,6 +58,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, "The /dev/zfs device is "
+                   "missing and must be created.\nTry running 'udevadm "
+                   "trigger' as root to create it.\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)
 {
@@ -570,7 +597,7 @@ zfs_nicenum(uint64_t num, char *buf, size_t buflen)
        int index = 0;
        char u;
 
-       while (n >= 1024) {
+       while (n >= 1024 && index < 6) {
                n /= 1024;
                index++;
        }
@@ -616,8 +643,8 @@ libzfs_module_loaded(const char *module)
        const char path_prefix[] = "/sys/module/";
        char path[256];
 
-       memcpy(path, path_prefix, sizeof(path_prefix) - 1);
-       strcpy(path + sizeof(path_prefix) - 1, module);
+       memcpy(path, path_prefix, sizeof (path_prefix) - 1);
+       strcpy(path + sizeof (path_prefix) - 1, module);
 
        return (access(path, F_OK) == 0);
 }
@@ -626,7 +653,7 @@ int
 libzfs_run_process(const char *path, char *argv[], int flags)
 {
        pid_t pid;
-       int rc, devnull_fd;
+       int error, devnull_fd;
 
        pid = vfork();
        if (pid == 0) {
@@ -648,37 +675,96 @@ libzfs_run_process(const char *path, char *argv[], int flags)
        } else if (pid > 0) {
                int status;
 
-               while ((rc = waitpid(pid, &status, 0)) == -1 &&
+               while ((error = waitpid(pid, &status, 0)) == -1 &&
                        errno == EINTR);
-               if (rc < 0 || !WIFEXITED(status))
-                       return -1;
+               if (error < 0 || !WIFEXITED(status))
+                       return (-1);
 
-               return WEXITSTATUS(status);
+               return (WEXITSTATUS(status));
        }
 
-       return -1;
+       return (-1);
 }
 
-int
+/*
+ * 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 (load && libzfs_run_process("/sbin/modprobe", argv, 0))
+                       return (ENOEXEC);
+       }
+
+       /* Module loading is synchronous it must be available */
+       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 */
+       }
 
-       if (libzfs_module_loaded(module))
-               return 0;
+       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 libzfs_run_process("/sbin/modprobe", argv, 0);
+       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);
        }
 
@@ -687,13 +773,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);
        }
@@ -710,6 +789,14 @@ libzfs_init(void)
 
        hdl->libzfs_sharetab = fopen("/etc/dfs/sharetab", "r");
 
+       if (libzfs_core_init() != 0) {
+               (void) close(hdl->libzfs_fd);
+               (void) fclose(hdl->libzfs_mnttab);
+               (void) fclose(hdl->libzfs_sharetab);
+               free(hdl);
+               return (NULL);
+       }
+
        zfs_prop_init();
        zpool_prop_init();
        zpool_feature_init();
@@ -731,12 +818,11 @@ libzfs_fini(libzfs_handle_t *hdl)
        if (hdl->libzfs_sharetab)
                (void) fclose(hdl->libzfs_sharetab);
        zfs_uninit_libshare(hdl);
-       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();
        free(hdl);
 }
 
@@ -783,7 +869,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) {
@@ -904,7 +993,7 @@ zfs_strcmp_shortname(char *name, char *cmp_name, int wholedisk)
                if (wholedisk)
                        path_len = zfs_append_partition(path_name, MAXPATHLEN);
 
-               if ((path_len == cmp_len) && !strcmp(path_name, cmp_name)) {
+               if ((path_len == cmp_len) && strcmp(path_name, cmp_name) == 0) {
                        error = 0;
                        break;
                }
@@ -935,21 +1024,23 @@ zfs_strcmp_pathname(char *name, char *cmp, int wholedisk)
        int path_len, cmp_len;
        char path_name[MAXPATHLEN];
        char cmp_name[MAXPATHLEN];
-       char *dir;
+       char *dir, *dup;
 
        /* Strip redundant slashes if one exists due to ZPOOL_IMPORT_PATH */
        memset(cmp_name, 0, MAXPATHLEN);
-       dir = strtok(cmp, "/");
+       dup = strdup(cmp);
+       dir = strtok(dup, "/");
        while (dir) {
                strcat(cmp_name, "/");
                strcat(cmp_name, dir);
                dir = strtok(NULL, "/");
        }
+       free(dup);
 
        if (name[0] != '/')
-               return zfs_strcmp_shortname(name, cmp_name, wholedisk);
+               return (zfs_strcmp_shortname(name, cmp_name, wholedisk));
 
-       strncpy(path_name, name, MAXPATHLEN);
+       (void) strlcpy(path_name, name, MAXPATHLEN);
        path_len = strlen(path_name);
        cmp_len = strlen(cmp_name);
 
@@ -1059,17 +1150,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));
 }
 
 /*
@@ -1271,7 +1352,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);
@@ -1313,10 +1395,10 @@ str2shift(libzfs_handle_t *hdl, const char *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);
+           ((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,
@@ -1475,6 +1557,16 @@ 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;
+               }
                break;
 
        case PROP_TYPE_INDEX:
@@ -1528,7 +1620,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;
 
        /*