/*
* 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.
*/
#include <wait.h>
#include <libzfs.h>
+#include <libzfs_core.h>
#include "libzfs_impl.h"
#include "zfs_prop.h"
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)
{
int index = 0;
char u;
- while (n >= 1024) {
+ while (n >= 1024 && index < 6) {
n /= 1024;
index++;
}
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);
}
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) {
} 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);
}
}
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);
}
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();
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);
}
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) {
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;
}
}
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);
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));
}
/*
*/
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,
"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:
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;
/*