summaryrefslogtreecommitdiff
path: root/lib/libzfs/os
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libzfs/os')
-rw-r--r--lib/libzfs/os/freebsd/libzfs_compat.c321
-rw-r--r--lib/libzfs/os/freebsd/libzfs_ioctl_compat.c432
-rw-r--r--lib/libzfs/os/freebsd/libzfs_zmount.c139
-rw-r--r--lib/libzfs/os/linux/libzfs_mount_os.c411
-rw-r--r--lib/libzfs/os/linux/libzfs_pool_os.c345
-rw-r--r--lib/libzfs/os/linux/libzfs_sendrecv_os.c52
-rw-r--r--lib/libzfs/os/linux/libzfs_util_os.c215
7 files changed, 1915 insertions, 0 deletions
diff --git a/lib/libzfs/os/freebsd/libzfs_compat.c b/lib/libzfs/os/freebsd/libzfs_compat.c
new file mode 100644
index 0000000000000..037ba56efe1c2
--- /dev/null
+++ b/lib/libzfs/os/freebsd/libzfs_compat.c
@@ -0,0 +1,321 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
+ */
+#include <os/freebsd/zfs/sys/zfs_ioctl_compat.h>
+#include <libzfs_impl.h>
+#include <libzfs.h>
+#include <libzutil.h>
+#include <sys/sysctl.h>
+#include <libintl.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#ifdef IN_BASE
+#define ZFS_KMOD "zfs"
+#else
+#define ZFS_KMOD "openzfs"
+#endif
+
+void
+libzfs_set_pipe_max(int infd)
+{
+ /* FreeBSD automatically resizes */
+}
+
+static int
+execvPe(const char *name, const char *path, char * const *argv,
+ char * const *envp)
+{
+ const char **memp;
+ size_t cnt, lp, ln;
+ int eacces, save_errno;
+ char *cur, buf[MAXPATHLEN];
+ const char *p, *bp;
+ struct stat sb;
+
+ eacces = 0;
+
+ /* If it's an absolute or relative path name, it's easy. */
+ if (strchr(name, '/')) {
+ bp = name;
+ cur = NULL;
+ goto retry;
+ }
+ bp = buf;
+
+ /* If it's an empty path name, fail in the usual POSIX way. */
+ if (*name == '\0') {
+ errno = ENOENT;
+ return (-1);
+ }
+
+ cur = alloca(strlen(path) + 1);
+ if (cur == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ strcpy(cur, path);
+ while ((p = strsep(&cur, ":")) != NULL) {
+ /*
+ * It's a SHELL path -- double, leading and trailing colons
+ * mean the current directory.
+ */
+ if (*p == '\0') {
+ p = ".";
+ lp = 1;
+ } else
+ lp = strlen(p);
+ ln = strlen(name);
+
+ /*
+ * If the path is too long complain. This is a possible
+ * security issue; given a way to make the path too long
+ * the user may execute the wrong program.
+ */
+ if (lp + ln + 2 > sizeof (buf)) {
+ (void) write(STDERR_FILENO, "execvP: ", 8);
+ (void) write(STDERR_FILENO, p, lp);
+ (void) write(STDERR_FILENO, ": path too long\n",
+ 16);
+ continue;
+ }
+ bcopy(p, buf, lp);
+ buf[lp] = '/';
+ bcopy(name, buf + lp + 1, ln);
+ buf[lp + ln + 1] = '\0';
+
+retry: (void) execve(bp, argv, envp);
+ switch (errno) {
+ case E2BIG:
+ goto done;
+ case ELOOP:
+ case ENAMETOOLONG:
+ case ENOENT:
+ break;
+ case ENOEXEC:
+ for (cnt = 0; argv[cnt]; ++cnt)
+ ;
+ memp = alloca((cnt + 2) * sizeof (char *));
+ if (memp == NULL) {
+ /* errno = ENOMEM; XXX override ENOEXEC? */
+ goto done;
+ }
+ memp[0] = "sh";
+ memp[1] = bp;
+ bcopy(argv + 1, memp + 2, cnt * sizeof (char *));
+ execve(_PATH_BSHELL, __DECONST(char **, memp), envp);
+ goto done;
+ case ENOMEM:
+ goto done;
+ case ENOTDIR:
+ break;
+ case ETXTBSY:
+ /*
+ * We used to retry here, but sh(1) doesn't.
+ */
+ goto done;
+ default:
+ /*
+ * EACCES may be for an inaccessible directory or
+ * a non-executable file. Call stat() to decide
+ * which. This also handles ambiguities for EFAULT
+ * and EIO, and undocumented errors like ESTALE.
+ * We hope that the race for a stat() is unimportant.
+ */
+ save_errno = errno;
+ if (stat(bp, &sb) != 0)
+ break;
+ if (save_errno == EACCES) {
+ eacces = 1;
+ continue;
+ }
+ errno = save_errno;
+ goto done;
+ }
+ }
+ if (eacces)
+ errno = EACCES;
+ else
+ errno = ENOENT;
+done:
+ return (-1);
+}
+
+int
+execvpe(const char *name, char * const argv[], char * const envp[])
+{
+ const char *path;
+
+ /* Get the path we're searching. */
+ if ((path = getenv("PATH")) == NULL)
+ path = _PATH_DEFPATH;
+
+ return (execvPe(name, path, argv, envp));
+}
+
+const char *
+libzfs_error_init(int error)
+{
+
+ return (strerror(error));
+}
+
+int
+zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
+{
+ return (zfs_ioctl_fd(hdl->libzfs_fd, request, zc));
+}
+
+/*
+ * 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
+ */
+int
+libzfs_load_module(void)
+{
+ /*
+ * XXX: kldfind(ZFS_KMOD) would be nice here, but we retain
+ * modfind("zfs") so out-of-base openzfs userland works with the
+ * in-base module.
+ */
+ if (modfind("zfs") < 0) {
+ /* Not present in kernel, try loading it. */
+ if (kldload(ZFS_KMOD) < 0 && errno != EEXIST) {
+ return (errno);
+ }
+ }
+ return (0);
+}
+
+int
+zpool_relabel_disk(libzfs_handle_t *hdl, const char *path, const char *msg)
+{
+ return (0);
+}
+
+int
+zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, const char *name)
+{
+ return (0);
+}
+
+int
+find_shares_object(differ_info_t *di)
+{
+ return (0);
+}
+
+/*
+ * Attach/detach the given filesystem to/from the given jail.
+ */
+int
+zfs_jail(zfs_handle_t *zhp, int jailid, int attach)
+{
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
+ zfs_cmd_t zc = {"\0"};
+ char errbuf[1024];
+ unsigned long cmd;
+ int ret;
+
+ if (attach) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name);
+ } else {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot unjail '%s'"), zhp->zfs_name);
+ }
+
+ switch (zhp->zfs_type) {
+ case ZFS_TYPE_VOLUME:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "volumes can not be jailed"));
+ return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
+ case ZFS_TYPE_SNAPSHOT:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "snapshots can not be jailed"));
+ return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
+ case ZFS_TYPE_BOOKMARK:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "bookmarks can not be jailed"));
+ return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
+ case ZFS_TYPE_POOL:
+ case ZFS_TYPE_FILESYSTEM:
+ /* OK */
+ ;
+ }
+ assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
+
+ (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
+ zc.zc_objset_type = DMU_OST_ZFS;
+ zc.zc_zoneid = jailid;
+
+ cmd = attach ? ZFS_IOC_JAIL : ZFS_IOC_UNJAIL;
+ if ((ret = zfs_ioctl(hdl, cmd, &zc)) != 0)
+ zfs_standard_error(hdl, errno, errbuf);
+
+ return (ret);
+}
+
+/*
+ * Set loader options for next boot.
+ */
+int
+zpool_nextboot(libzfs_handle_t *hdl, uint64_t pool_guid, uint64_t dev_guid,
+ const char *command)
+{
+ zfs_cmd_t zc = {"\0"};
+ nvlist_t *args;
+ int error;
+
+ args = fnvlist_alloc();
+ fnvlist_add_uint64(args, ZPOOL_CONFIG_POOL_GUID, pool_guid);
+ fnvlist_add_uint64(args, ZPOOL_CONFIG_GUID, dev_guid);
+ fnvlist_add_string(args, "command", command);
+ error = zcmd_write_src_nvlist(hdl, &zc, args);
+ if (error == 0)
+ error = zfs_ioctl(hdl, ZFS_IOC_NEXTBOOT, &zc);
+ zcmd_free_nvlists(&zc);
+ nvlist_free(args);
+ return (error);
+}
+
+/*
+ * Fill given version buffer with zfs kernel version.
+ * Returns 0 on success, and -1 on error (with errno set)
+ */
+int
+zfs_version_kernel(char *version, int len)
+{
+ size_t l = len;
+
+ return (sysctlbyname("vfs.zfs.version.module",
+ version, &l, NULL, 0));
+}
diff --git a/lib/libzfs/os/freebsd/libzfs_ioctl_compat.c b/lib/libzfs/os/freebsd/libzfs_ioctl_compat.c
new file mode 100644
index 0000000000000..18b93fe279695
--- /dev/null
+++ b/lib/libzfs/os/freebsd/libzfs_ioctl_compat.c
@@ -0,0 +1,432 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2013 Xin Li <delphij@FreeBSD.org>. All rights reserved.
+ * Copyright 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
+ * Portions Copyright 2005, 2010, Oracle and/or its affiliates.
+ * All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/cred.h>
+#include <sys/dmu.h>
+#include <sys/zio.h>
+#include <sys/nvpair.h>
+#include <sys/dsl_deleg.h>
+#include <sys/zfs_ioctl.h>
+#include "zfs_namecheck.h"
+#include <os/freebsd/zfs/sys/zfs_ioctl_compat.h>
+
+/*
+ * FreeBSD zfs_cmd compatibility with older binaries
+ * appropriately remap/extend the zfs_cmd_t structure
+ */
+void
+zfs_cmd_compat_get(zfs_cmd_t *zc, caddr_t addr, const int cflag)
+{
+
+}
+#if 0
+static int
+zfs_ioctl_compat_get_nvlist(uint64_t nvl, size_t size, int iflag,
+ nvlist_t **nvp)
+{
+ char *packed;
+ int error;
+ nvlist_t *list = NULL;
+
+ /*
+ * Read in and unpack the user-supplied nvlist.
+ */
+ if (size == 0)
+ return (EINVAL);
+
+#ifdef _KERNEL
+ packed = kmem_alloc(size, KM_SLEEP);
+ if ((error = ddi_copyin((void *)(uintptr_t)nvl, packed, size,
+ iflag)) != 0) {
+ kmem_free(packed, size);
+ return (error);
+ }
+#else
+ packed = (void *)(uintptr_t)nvl;
+#endif
+
+ error = nvlist_unpack(packed, size, &list, 0);
+
+#ifdef _KERNEL
+ kmem_free(packed, size);
+#endif
+
+ if (error != 0)
+ return (error);
+
+ *nvp = list;
+ return (0);
+}
+
+static int
+zfs_ioctl_compat_put_nvlist(zfs_cmd_t *zc, nvlist_t *nvl)
+{
+ char *packed = NULL;
+ int error = 0;
+ size_t size;
+
+ VERIFY(nvlist_size(nvl, &size, NV_ENCODE_NATIVE) == 0);
+
+#ifdef _KERNEL
+ packed = kmem_alloc(size, KM_SLEEP);
+ VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
+ KM_SLEEP) == 0);
+
+ if (ddi_copyout(packed,
+ (void *)(uintptr_t)zc->zc_nvlist_dst, size, zc->zc_iflags) != 0)
+ error = EFAULT;
+ kmem_free(packed, size);
+#else
+ packed = (void *)(uintptr_t)zc->zc_nvlist_dst;
+ VERIFY(nvlist_pack(nvl, &packed, &size, NV_ENCODE_NATIVE,
+ 0) == 0);
+#endif
+
+ zc->zc_nvlist_dst_size = size;
+ return (error);
+}
+
+static void
+zfs_ioctl_compat_fix_stats_nvlist(nvlist_t *nvl)
+{
+ nvlist_t **child;
+ nvlist_t *nvroot = NULL;
+ vdev_stat_t *vs;
+ uint_t c, children, nelem;
+
+ if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_CHILDREN,
+ &child, &children) == 0) {
+ for (c = 0; c < children; c++) {
+ zfs_ioctl_compat_fix_stats_nvlist(child[c]);
+ }
+ }
+
+ if (nvlist_lookup_nvlist(nvl, ZPOOL_CONFIG_VDEV_TREE,
+ &nvroot) == 0)
+ zfs_ioctl_compat_fix_stats_nvlist(nvroot);
+ if ((nvlist_lookup_uint64_array(nvl, "stats",
+ (uint64_t **)&vs, &nelem) == 0)) {
+ nvlist_add_uint64_array(nvl,
+ ZPOOL_CONFIG_VDEV_STATS,
+ (uint64_t *)vs, nelem);
+ nvlist_remove(nvl, "stats",
+ DATA_TYPE_UINT64_ARRAY);
+ }
+}
+
+
+static int
+zfs_ioctl_compat_fix_stats(zfs_cmd_t *zc, const int nc)
+{
+ nvlist_t *nv, *nvp = NULL;
+ nvpair_t *elem;
+ int error;
+
+ if ((error = zfs_ioctl_compat_get_nvlist(zc->zc_nvlist_dst,
+ zc->zc_nvlist_dst_size, zc->zc_iflags, &nv)) != 0)
+ return (error);
+
+ if (nc == 5) { /* ZFS_IOC_POOL_STATS */
+ elem = NULL;
+ while ((elem = nvlist_next_nvpair(nv, elem)) != NULL) {
+ if (nvpair_value_nvlist(elem, &nvp) == 0)
+ zfs_ioctl_compat_fix_stats_nvlist(nvp);
+ }
+ elem = NULL;
+ } else
+ zfs_ioctl_compat_fix_stats_nvlist(nv);
+
+ error = zfs_ioctl_compat_put_nvlist(zc, nv);
+
+ nvlist_free(nv);
+
+ return (error);
+}
+
+static int
+zfs_ioctl_compat_pool_get_props(zfs_cmd_t *zc)
+{
+ nvlist_t *nv, *nva = NULL;
+ int error;
+
+ if ((error = zfs_ioctl_compat_get_nvlist(zc->zc_nvlist_dst,
+ zc->zc_nvlist_dst_size, zc->zc_iflags, &nv)) != 0)
+ return (error);
+
+ if (nvlist_lookup_nvlist(nv, "used", &nva) == 0) {
+ nvlist_add_nvlist(nv, "allocated", nva);
+ nvlist_remove(nv, "used", DATA_TYPE_NVLIST);
+ }
+
+ if (nvlist_lookup_nvlist(nv, "available", &nva) == 0) {
+ nvlist_add_nvlist(nv, "free", nva);
+ nvlist_remove(nv, "available", DATA_TYPE_NVLIST);
+ }
+
+ error = zfs_ioctl_compat_put_nvlist(zc, nv);
+
+ nvlist_free(nv);
+
+ return (error);
+}
+#endif
+
+#ifdef _KERNEL
+int
+zfs_ioctl_compat_pre(zfs_cmd_t *zc, int *vec, const int cflag)
+{
+ int error = 0;
+
+ /* are we creating a clone? */
+ if (*vec == ZFS_IOC_CREATE && zc->zc_value[0] != '\0')
+ *vec = ZFS_IOC_CLONE;
+
+ if (cflag == ZFS_CMD_COMPAT_V15) {
+ switch (*vec) {
+
+ case 7: /* ZFS_IOC_POOL_SCRUB (v15) */
+ zc->zc_cookie = POOL_SCAN_SCRUB;
+ break;
+ }
+ }
+
+ return (error);
+}
+
+void
+zfs_ioctl_compat_post(zfs_cmd_t *zc, int vec, const int cflag)
+{
+ if (cflag == ZFS_CMD_COMPAT_V15) {
+ switch (vec) {
+ case ZFS_IOC_POOL_CONFIGS:
+ case ZFS_IOC_POOL_STATS:
+ case ZFS_IOC_POOL_TRYIMPORT:
+ zfs_ioctl_compat_fix_stats(zc, vec);
+ break;
+ case 41: /* ZFS_IOC_POOL_GET_PROPS (v15) */
+ zfs_ioctl_compat_pool_get_props(zc);
+ break;
+ }
+ }
+}
+
+nvlist_t *
+zfs_ioctl_compat_innvl(zfs_cmd_t *zc, nvlist_t *innvl, const int vec,
+ const int cflag)
+{
+ nvlist_t *nvl, *tmpnvl, *hnvl;
+ nvpair_t *elem;
+ char *poolname, *snapname;
+ int err;
+
+ if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC ||
+ cflag == ZFS_CMD_COMPAT_ZCMD || cflag == ZFS_CMD_COMPAT_EDBP ||
+ cflag == ZFS_CMD_COMPAT_RESUME || cflag == ZFS_CMD_COMPAT_INLANES)
+ goto out;
+
+ switch (vec) {
+ case ZFS_IOC_CREATE:
+ nvl = fnvlist_alloc();
+ fnvlist_add_int32(nvl, "type", zc->zc_objset_type);
+ if (innvl != NULL) {
+ fnvlist_add_nvlist(nvl, "props", innvl);
+ nvlist_free(innvl);
+ }
+ return (nvl);
+ break;
+ case ZFS_IOC_CLONE:
+ nvl = fnvlist_alloc();
+ fnvlist_add_string(nvl, "origin", zc->zc_value);
+ if (innvl != NULL) {
+ fnvlist_add_nvlist(nvl, "props", innvl);
+ nvlist_free(innvl);
+ }
+ return (nvl);
+ break;
+ case ZFS_IOC_SNAPSHOT:
+ if (innvl == NULL)
+ goto out;
+ nvl = fnvlist_alloc();
+ fnvlist_add_nvlist(nvl, "props", innvl);
+ tmpnvl = fnvlist_alloc();
+ snapname = kmem_asprintf("%s@%s", zc->zc_name, zc->zc_value);
+ fnvlist_add_boolean(tmpnvl, snapname);
+ kmem_free(snapname, strlen(snapname + 1));
+ /* check if we are doing a recursive snapshot */
+ if (zc->zc_cookie)
+ dmu_get_recursive_snaps_nvl(zc->zc_name, zc->zc_value,
+ tmpnvl);
+ fnvlist_add_nvlist(nvl, "snaps", tmpnvl);
+ fnvlist_free(tmpnvl);
+ nvlist_free(innvl);
+ /* strip dataset part from zc->zc_name */
+ zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
+ return (nvl);
+ break;
+ case ZFS_IOC_SPACE_SNAPS:
+ nvl = fnvlist_alloc();
+ fnvlist_add_string(nvl, "firstsnap", zc->zc_value);
+ if (innvl != NULL)
+ nvlist_free(innvl);
+ return (nvl);
+ break;
+ case ZFS_IOC_DESTROY_SNAPS:
+ if (innvl == NULL && cflag == ZFS_CMD_COMPAT_DEADMAN)
+ goto out;
+ nvl = fnvlist_alloc();
+ if (innvl != NULL) {
+ fnvlist_add_nvlist(nvl, "snaps", innvl);
+ } else {
+ /*
+ * We are probably called by even older binaries,
+ * allocate and populate nvlist with recursive
+ * snapshots
+ */
+ if (zfs_component_namecheck(zc->zc_value, NULL,
+ NULL) == 0) {
+ tmpnvl = fnvlist_alloc();
+ if (dmu_get_recursive_snaps_nvl(zc->zc_name,
+ zc->zc_value, tmpnvl) == 0)
+ fnvlist_add_nvlist(nvl, "snaps",
+ tmpnvl);
+ nvlist_free(tmpnvl);
+ }
+ }
+ if (innvl != NULL)
+ nvlist_free(innvl);
+ /* strip dataset part from zc->zc_name */
+ zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
+ return (nvl);
+ break;
+ case ZFS_IOC_HOLD:
+ nvl = fnvlist_alloc();
+ tmpnvl = fnvlist_alloc();
+ if (zc->zc_cleanup_fd != -1)
+ fnvlist_add_int32(nvl, "cleanup_fd",
+ (int32_t)zc->zc_cleanup_fd);
+ if (zc->zc_cookie) {
+ hnvl = fnvlist_alloc();
+ if (dmu_get_recursive_snaps_nvl(zc->zc_name,
+ zc->zc_value, hnvl) == 0) {
+ elem = NULL;
+ while ((elem = nvlist_next_nvpair(hnvl,
+ elem)) != NULL) {
+ nvlist_add_string(tmpnvl,
+ nvpair_name(elem), zc->zc_string);
+ }
+ }
+ nvlist_free(hnvl);
+ } else {
+ snapname = kmem_asprintf("%s@%s", zc->zc_name,
+ zc->zc_value);
+ nvlist_add_string(tmpnvl, snapname, zc->zc_string);
+ kmem_free(snapname, strlen(snapname + 1));
+ }
+ fnvlist_add_nvlist(nvl, "holds", tmpnvl);
+ nvlist_free(tmpnvl);
+ if (innvl != NULL)
+ nvlist_free(innvl);
+ /* strip dataset part from zc->zc_name */
+ zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
+ return (nvl);
+ break;
+ case ZFS_IOC_RELEASE:
+ nvl = fnvlist_alloc();
+ tmpnvl = fnvlist_alloc();
+ if (zc->zc_cookie) {
+ hnvl = fnvlist_alloc();
+ if (dmu_get_recursive_snaps_nvl(zc->zc_name,
+ zc->zc_value, hnvl) == 0) {
+ elem = NULL;
+ while ((elem = nvlist_next_nvpair(hnvl,
+ elem)) != NULL) {
+ fnvlist_add_boolean(tmpnvl,
+ zc->zc_string);
+ fnvlist_add_nvlist(nvl,
+ nvpair_name(elem), tmpnvl);
+ }
+ }
+ nvlist_free(hnvl);
+ } else {
+ snapname = kmem_asprintf("%s@%s", zc->zc_name,
+ zc->zc_value);
+ fnvlist_add_boolean(tmpnvl, zc->zc_string);
+ fnvlist_add_nvlist(nvl, snapname, tmpnvl);
+ kmem_free(snapname, strlen(snapname + 1));
+ }
+ nvlist_free(tmpnvl);
+ if (innvl != NULL)
+ nvlist_free(innvl);
+ /* strip dataset part from zc->zc_name */
+ zc->zc_name[strcspn(zc->zc_name, "/@")] = '\0';
+ return (nvl);
+ break;
+ }
+out:
+ return (innvl);
+}
+
+nvlist_t *
+zfs_ioctl_compat_outnvl(zfs_cmd_t *zc, nvlist_t *outnvl, const int vec,
+ const int cflag)
+{
+ nvlist_t *tmpnvl;
+
+ if (cflag == ZFS_CMD_COMPAT_NONE || cflag == ZFS_CMD_COMPAT_LZC ||
+ cflag == ZFS_CMD_COMPAT_ZCMD || cflag == ZFS_CMD_COMPAT_EDBP ||
+ cflag == ZFS_CMD_COMPAT_RESUME || cflag == ZFS_CMD_COMPAT_INLANES)
+ return (outnvl);
+
+ switch (vec) {
+ case ZFS_IOC_SPACE_SNAPS:
+ (void) nvlist_lookup_uint64(outnvl, "used", &zc->zc_cookie);
+ (void) nvlist_lookup_uint64(outnvl, "compressed",
+ &zc->zc_objset_type);
+ (void) nvlist_lookup_uint64(outnvl, "uncompressed",
+ &zc->zc_perm_action);
+ nvlist_free(outnvl);
+ /* return empty outnvl */
+ tmpnvl = fnvlist_alloc();
+ return (tmpnvl);
+ break;
+ case ZFS_IOC_CREATE:
+ case ZFS_IOC_CLONE:
+ case ZFS_IOC_HOLD:
+ case ZFS_IOC_RELEASE:
+ nvlist_free(outnvl);
+ /* return empty outnvl */
+ tmpnvl = fnvlist_alloc();
+ return (tmpnvl);
+ break;
+ }
+
+ return (outnvl);
+}
+#endif /* KERNEL */
diff --git a/lib/libzfs/os/freebsd/libzfs_zmount.c b/lib/libzfs/os/freebsd/libzfs_zmount.c
new file mode 100644
index 0000000000000..2207fffc55734
--- /dev/null
+++ b/lib/libzfs/os/freebsd/libzfs_zmount.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This file implements Solaris compatible zmount() function.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/uio.h>
+#include <sys/mntent.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mnttab.h>
+#include <sys/errno.h>
+#include <libzfs.h>
+
+#include "libzfs_impl.h"
+
+static void
+build_iovec(struct iovec **iov, int *iovlen, const char *name, void *val,
+ size_t len)
+{
+ int i;
+
+ if (*iovlen < 0)
+ return;
+ i = *iovlen;
+ *iov = realloc(*iov, sizeof (**iov) * (i + 2));
+ if (*iov == NULL) {
+ *iovlen = -1;
+ return;
+ }
+ (*iov)[i].iov_base = strdup(name);
+ (*iov)[i].iov_len = strlen(name) + 1;
+ i++;
+ (*iov)[i].iov_base = val;
+ if (len == (size_t)-1) {
+ if (val != NULL)
+ len = strlen(val) + 1;
+ else
+ len = 0;
+ }
+ (*iov)[i].iov_len = (int)len;
+ *iovlen = ++i;
+}
+
+static int
+do_mount_(const char *spec, const char *dir, int mflag, char *fstype,
+ char *dataptr, int datalen, char *optptr, int optlen)
+{
+ struct iovec *iov;
+ char *optstr, *p, *tofree;
+ int iovlen, rv;
+
+ assert(spec != NULL);
+ assert(dir != NULL);
+ assert(fstype != NULL);
+ assert(strcmp(fstype, MNTTYPE_ZFS) == 0);
+ assert(dataptr == NULL);
+ assert(datalen == 0);
+ assert(optptr != NULL);
+ assert(optlen > 0);
+
+ tofree = optstr = strdup(optptr);
+ assert(optstr != NULL);
+
+ iov = NULL;
+ iovlen = 0;
+ if (strstr(optstr, MNTOPT_REMOUNT) != NULL)
+ build_iovec(&iov, &iovlen, "update", NULL, 0);
+ if (strstr(optstr, MNTOPT_NOXATTR) == NULL &&
+ strstr(optstr, MNTOPT_XATTR) == NULL &&
+ strstr(optstr, MNTOPT_SAXATTR) == NULL &&
+ strstr(optstr, MNTOPT_DIRXATTR) == NULL)
+ build_iovec(&iov, &iovlen, "xattr", NULL, 0);
+ if (mflag & MS_RDONLY)
+ build_iovec(&iov, &iovlen, "ro", NULL, 0);
+ build_iovec(&iov, &iovlen, "fstype", fstype, (size_t)-1);
+ build_iovec(&iov, &iovlen, "fspath", __DECONST(char *, dir),
+ (size_t)-1);
+ build_iovec(&iov, &iovlen, "from", __DECONST(char *, spec), (size_t)-1);
+ while ((p = strsep(&optstr, ",/")) != NULL)
+ build_iovec(&iov, &iovlen, p, NULL, (size_t)-1);
+ rv = nmount(iov, iovlen, 0);
+ free(tofree);
+ if (rv < 0)
+ return (errno);
+ return (rv);
+}
+
+int
+do_mount(zfs_handle_t *zhp, const char *mntpt, char *opts, int flags)
+{
+
+ return (do_mount_(zfs_get_name(zhp), mntpt, flags, MNTTYPE_ZFS, NULL, 0,
+ opts, sizeof (mntpt)));
+}
+
+int
+do_unmount(const char *mntpt, int flags)
+{
+
+ return (unmount(mntpt, flags));
+}
+
+int
+zfs_mount_delegation_check(void)
+{
+ return (0);
+}
diff --git a/lib/libzfs/os/linux/libzfs_mount_os.c b/lib/libzfs/os/linux/libzfs_mount_os.c
new file mode 100644
index 0000000000000..92052f28ffe77
--- /dev/null
+++ b/lib/libzfs/os/linux/libzfs_mount_os.c
@@ -0,0 +1,411 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2020 by Delphix. All rights reserved.
+ * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
+ * Copyright 2017 RackTop Systems.
+ * Copyright (c) 2018 Datto Inc.
+ * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
+ */
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <zone.h>
+#include <sys/mntent.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <sys/dsl_crypt.h>
+#include <libzfs.h>
+
+#include "libzfs_impl.h"
+#include <thread_pool.h>
+
+#define ZS_COMMENT 0x00000000 /* comment */
+#define ZS_ZFSUTIL 0x00000001 /* caller is zfs(8) */
+
+typedef struct option_map {
+ const char *name;
+ unsigned long mntmask;
+ unsigned long zfsmask;
+} option_map_t;
+
+static const option_map_t option_map[] = {
+ /* Canonicalized filesystem independent options from mount(8) */
+ { MNTOPT_NOAUTO, MS_COMMENT, ZS_COMMENT },
+ { MNTOPT_DEFAULTS, MS_COMMENT, ZS_COMMENT },
+ { MNTOPT_NODEVICES, MS_NODEV, ZS_COMMENT },
+ { MNTOPT_DEVICES, MS_COMMENT, ZS_COMMENT },
+ { MNTOPT_DIRSYNC, MS_DIRSYNC, ZS_COMMENT },
+ { MNTOPT_NOEXEC, MS_NOEXEC, ZS_COMMENT },
+ { MNTOPT_EXEC, MS_COMMENT, ZS_COMMENT },
+ { MNTOPT_GROUP, MS_GROUP, ZS_COMMENT },
+ { MNTOPT_NETDEV, MS_COMMENT, ZS_COMMENT },
+ { MNTOPT_NOFAIL, MS_COMMENT, ZS_COMMENT },
+ { MNTOPT_NOSUID, MS_NOSUID, ZS_COMMENT },
+ { MNTOPT_SUID, MS_COMMENT, ZS_COMMENT },
+ { MNTOPT_OWNER, MS_OWNER, ZS_COMMENT },
+ { MNTOPT_REMOUNT, MS_REMOUNT, ZS_COMMENT },
+ { MNTOPT_RO, MS_RDONLY, ZS_COMMENT },
+ { MNTOPT_RW, MS_COMMENT, ZS_COMMENT },
+ { MNTOPT_SYNC, MS_SYNCHRONOUS, ZS_COMMENT },
+ { MNTOPT_USER, MS_USERS, ZS_COMMENT },
+ { MNTOPT_USERS, MS_USERS, ZS_COMMENT },
+ /* acl flags passed with util-linux-2.24 mount command */
+ { MNTOPT_ACL, MS_POSIXACL, ZS_COMMENT },
+ { MNTOPT_NOACL, MS_COMMENT, ZS_COMMENT },
+ { MNTOPT_POSIXACL, MS_POSIXACL, ZS_COMMENT },
+#ifdef MS_NOATIME
+ { MNTOPT_NOATIME, MS_NOATIME, ZS_COMMENT },
+ { MNTOPT_ATIME, MS_COMMENT, ZS_COMMENT },
+#endif
+#ifdef MS_NODIRATIME
+ { MNTOPT_NODIRATIME, MS_NODIRATIME, ZS_COMMENT },
+ { MNTOPT_DIRATIME, MS_COMMENT, ZS_COMMENT },
+#endif
+#ifdef MS_RELATIME
+ { MNTOPT_RELATIME, MS_RELATIME, ZS_COMMENT },
+ { MNTOPT_NORELATIME, MS_COMMENT, ZS_COMMENT },
+#endif
+#ifdef MS_STRICTATIME
+ { MNTOPT_STRICTATIME, MS_STRICTATIME, ZS_COMMENT },
+ { MNTOPT_NOSTRICTATIME, MS_COMMENT, ZS_COMMENT },
+#endif
+#ifdef MS_LAZYTIME
+ { MNTOPT_LAZYTIME, MS_LAZYTIME, ZS_COMMENT },
+#endif
+ { MNTOPT_CONTEXT, MS_COMMENT, ZS_COMMENT },
+ { MNTOPT_FSCONTEXT, MS_COMMENT, ZS_COMMENT },
+ { MNTOPT_DEFCONTEXT, MS_COMMENT, ZS_COMMENT },
+ { MNTOPT_ROOTCONTEXT, MS_COMMENT, ZS_COMMENT },
+#ifdef MS_I_VERSION
+ { MNTOPT_IVERSION, MS_I_VERSION, ZS_COMMENT },
+#endif
+#ifdef MS_MANDLOCK
+ { MNTOPT_NBMAND, MS_MANDLOCK, ZS_COMMENT },
+ { MNTOPT_NONBMAND, MS_COMMENT, ZS_COMMENT },
+#endif
+ /* Valid options not found in mount(8) */
+ { MNTOPT_BIND, MS_BIND, ZS_COMMENT },
+#ifdef MS_REC
+ { MNTOPT_RBIND, MS_BIND|MS_REC, ZS_COMMENT },
+#endif
+ { MNTOPT_COMMENT, MS_COMMENT, ZS_COMMENT },
+#ifdef MS_NOSUB
+ { MNTOPT_NOSUB, MS_NOSUB, ZS_COMMENT },
+#endif
+#ifdef MS_SILENT
+ { MNTOPT_QUIET, MS_SILENT, ZS_COMMENT },
+#endif
+ /* Custom zfs options */
+ { MNTOPT_XATTR, MS_COMMENT, ZS_COMMENT },
+ { MNTOPT_NOXATTR, MS_COMMENT, ZS_COMMENT },
+ { MNTOPT_ZFSUTIL, MS_COMMENT, ZS_ZFSUTIL },
+ { NULL, 0, 0 } };
+
+/*
+ * Break the mount option in to a name/value pair. The name is
+ * validated against the option map and mount flags set accordingly.
+ */
+static int
+parse_option(char *mntopt, unsigned long *mntflags,
+ unsigned long *zfsflags, int sloppy)
+{
+ const option_map_t *opt;
+ char *ptr, *name, *value = NULL;
+ int error = 0;
+
+ name = strdup(mntopt);
+ if (name == NULL)
+ return (ENOMEM);
+
+ for (ptr = name; ptr && *ptr; ptr++) {
+ if (*ptr == '=') {
+ *ptr = '\0';
+ value = ptr+1;
+ VERIFY3P(value, !=, NULL);
+ break;
+ }
+ }
+
+ for (opt = option_map; opt->name != NULL; opt++) {
+ if (strncmp(name, opt->name, strlen(name)) == 0) {
+ *mntflags |= opt->mntmask;
+ *zfsflags |= opt->zfsmask;
+ error = 0;
+ goto out;
+ }
+ }
+
+ if (!sloppy)
+ error = ENOENT;
+out:
+ /* If required further process on the value may be done here */
+ free(name);
+ return (error);
+}
+
+/*
+ * Translate the mount option string in to MS_* mount flags for the
+ * kernel vfs. When sloppy is non-zero unknown options will be ignored
+ * otherwise they are considered fatal are copied in to badopt.
+ */
+int
+zfs_parse_mount_options(char *mntopts, unsigned long *mntflags,
+ unsigned long *zfsflags, int sloppy, char *badopt, char *mtabopt)
+{
+ int error = 0, quote = 0, flag = 0, count = 0;
+ char *ptr, *opt, *opts;
+
+ opts = strdup(mntopts);
+ if (opts == NULL)
+ return (ENOMEM);
+
+ *mntflags = 0;
+ opt = NULL;
+
+ /*
+ * Scan through all mount options which must be comma delimited.
+ * We must be careful to notice regions which are double quoted
+ * and skip commas in these regions. Each option is then checked
+ * to determine if it is a known option.
+ */
+ for (ptr = opts; ptr && !flag; ptr++) {
+ if (opt == NULL)
+ opt = ptr;
+
+ if (*ptr == '"')
+ quote = !quote;
+
+ if (quote)
+ continue;
+
+ if (*ptr == '\0')
+ flag = 1;
+
+ if ((*ptr == ',') || (*ptr == '\0')) {
+ *ptr = '\0';
+
+ error = parse_option(opt, mntflags, zfsflags, sloppy);
+ if (error) {
+ strcpy(badopt, opt);
+ goto out;
+
+ }
+
+ if (!(*mntflags & MS_REMOUNT) &&
+ !(*zfsflags & ZS_ZFSUTIL) &&
+ mtabopt != NULL) {
+ if (count > 0)
+ strlcat(mtabopt, ",", MNT_LINE_MAX);
+
+ strlcat(mtabopt, opt, MNT_LINE_MAX);
+ count++;
+ }
+
+ opt = NULL;
+ }
+ }
+
+out:
+ free(opts);
+ return (error);
+}
+
+static void
+append_mntopt(const char *name, const char *val, char *mntopts,
+ char *mtabopt, boolean_t quote)
+{
+ char tmp[MNT_LINE_MAX];
+
+ snprintf(tmp, MNT_LINE_MAX, quote ? ",%s=\"%s\"" : ",%s=%s", name, val);
+
+ if (mntopts)
+ strlcat(mntopts, tmp, MNT_LINE_MAX);
+
+ if (mtabopt)
+ strlcat(mtabopt, tmp, MNT_LINE_MAX);
+}
+
+static void
+zfs_selinux_setcontext(zfs_handle_t *zhp, zfs_prop_t zpt, const char *name,
+ char *mntopts, char *mtabopt)
+{
+ char context[ZFS_MAXPROPLEN];
+
+ if (zfs_prop_get(zhp, zpt, context, sizeof (context),
+ NULL, NULL, 0, B_FALSE) == 0) {
+ if (strcmp(context, "none") != 0)
+ append_mntopt(name, context, mntopts, mtabopt, B_TRUE);
+ }
+}
+
+void
+zfs_adjust_mount_options(zfs_handle_t *zhp, const char *mntpoint,
+ char *mntopts, char *mtabopt)
+{
+ char prop[ZFS_MAXPROPLEN];
+
+ /*
+ * Checks to see if the ZFS_PROP_SELINUX_CONTEXT exists
+ * if it does, create a tmp variable in case it's needed
+ * checks to see if the selinux context is set to the default
+ * if it is, allow the setting of the other context properties
+ * this is needed because the 'context' property overrides others
+ * if it is not the default, set the 'context' property
+ */
+ if (zfs_prop_get(zhp, ZFS_PROP_SELINUX_CONTEXT, prop, sizeof (prop),
+ NULL, NULL, 0, B_FALSE) == 0) {
+ if (strcmp(prop, "none") == 0) {
+ zfs_selinux_setcontext(zhp, ZFS_PROP_SELINUX_FSCONTEXT,
+ MNTOPT_FSCONTEXT, mntopts, mtabopt);
+ zfs_selinux_setcontext(zhp, ZFS_PROP_SELINUX_DEFCONTEXT,
+ MNTOPT_DEFCONTEXT, mntopts, mtabopt);
+ zfs_selinux_setcontext(zhp,
+ ZFS_PROP_SELINUX_ROOTCONTEXT, MNTOPT_ROOTCONTEXT,
+ mntopts, mtabopt);
+ } else {
+ append_mntopt(MNTOPT_CONTEXT, prop,
+ mntopts, mtabopt, B_TRUE);
+ }
+ }
+
+ /* A hint used to determine an auto-mounted snapshot mount point */
+ append_mntopt(MNTOPT_MNTPOINT, mntpoint, mntopts, NULL, B_FALSE);
+}
+
+/*
+ * By default the filesystem by preparing the mount options (i.e. parsing
+ * some flags from the "opts" parameter into the "flags" parameter) and then
+ * directly calling the system call mount(2). We don't need the mount utility
+ * or update /etc/mtab, because this is a symlink on all modern systems.
+ *
+ * If the environment variable ZFS_MOUNT_HELPER is set, we fall back to the
+ * previous behavior:
+ * The filesystem is mounted by invoking the system mount utility rather
+ * than by the system call mount(2). This ensures that the /etc/mtab
+ * file is correctly locked for the update. Performing our own locking
+ * and /etc/mtab update requires making an unsafe assumption about how
+ * the mount utility performs its locking. Unfortunately, this also means
+ * in the case of a mount failure we do not have the exact errno. We must
+ * make due with return value from the mount process.
+ */
+int
+do_mount(zfs_handle_t *zhp, const char *mntpt, char *opts, int flags)
+{
+ const char *src = zfs_get_name(zhp);
+ int error = 0;
+
+ if (!libzfs_envvar_is_set("ZFS_MOUNT_HELPER")) {
+ char badopt[MNT_LINE_MAX] = {0};
+ unsigned long mntflags = flags, zfsflags;
+ char myopts[MNT_LINE_MAX] = {0};
+
+ if (zfs_parse_mount_options(opts, &mntflags,
+ &zfsflags, 0, badopt, NULL)) {
+ return (EINVAL);
+ }
+ strlcat(myopts, opts, MNT_LINE_MAX);
+ zfs_adjust_mount_options(zhp, mntpt, myopts, NULL);
+ if (mount(src, mntpt, MNTTYPE_ZFS, mntflags, myopts)) {
+ return (errno);
+ }
+ } else {
+ char *argv[9] = {
+ "/bin/mount",
+ "--no-canonicalize",
+ "-t", MNTTYPE_ZFS,
+ "-o", opts,
+ (char *)src,
+ (char *)mntpt,
+ (char *)NULL };
+
+ /* Return only the most critical mount error */
+ error = libzfs_run_process(argv[0], argv,
+ STDOUT_VERBOSE|STDERR_VERBOSE);
+ if (error) {
+ if (error & MOUNT_FILEIO) {
+ error = EIO;
+ } else if (error & MOUNT_USER) {
+ error = EINTR;
+ } else if (error & MOUNT_SOFTWARE) {
+ error = EPIPE;
+ } else if (error & MOUNT_BUSY) {
+ error = EBUSY;
+ } else if (error & MOUNT_SYSERR) {
+ error = EAGAIN;
+ } else if (error & MOUNT_USAGE) {
+ error = EINVAL;
+ } else
+ error = ENXIO; /* Generic error */
+ }
+ }
+
+ return (error);
+}
+
+int
+do_unmount(const char *mntpt, int flags)
+{
+ if (!libzfs_envvar_is_set("ZFS_MOUNT_HELPER")) {
+ return (umount2(mntpt, flags));
+ }
+
+ char force_opt[] = "-f";
+ char lazy_opt[] = "-l";
+ char *argv[7] = {
+ "/bin/umount",
+ "-t", MNTTYPE_ZFS,
+ NULL, NULL, NULL, NULL };
+ int rc, count = 3;
+
+ if (flags & MS_FORCE) {
+ argv[count] = force_opt;
+ count++;
+ }
+
+ if (flags & MS_DETACH) {
+ argv[count] = lazy_opt;
+ count++;
+ }
+
+ argv[count] = (char *)mntpt;
+ rc = libzfs_run_process(argv[0], argv, STDOUT_VERBOSE|STDERR_VERBOSE);
+
+ return (rc ? EINVAL : 0);
+}
+
+int
+zfs_mount_delegation_check(void)
+{
+ return ((geteuid() != 0) ? EACCES : 0);
+}
diff --git a/lib/libzfs/os/linux/libzfs_pool_os.c b/lib/libzfs/os/linux/libzfs_pool_os.c
new file mode 100644
index 0000000000000..5c6da5338dbc3
--- /dev/null
+++ b/lib/libzfs/os/linux/libzfs_pool_os.c
@@ -0,0 +1,345 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2018 by Delphix. All rights reserved.
+ * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
+ * Copyright (c) 2018 Datto Inc.
+ * Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
+ * Copyright (c) 2017, Intel Corporation.
+ * Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>
+ */
+
+#include <errno.h>
+#include <libintl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <zone.h>
+#include <sys/stat.h>
+#include <sys/efi_partition.h>
+#include <sys/systeminfo.h>
+#include <sys/vtoc.h>
+#include <sys/zfs_ioctl.h>
+#include <sys/vdev_disk.h>
+#include <dlfcn.h>
+#include <libzutil.h>
+
+#include "zfs_namecheck.h"
+#include "zfs_prop.h"
+#include "libzfs_impl.h"
+#include "zfs_comutil.h"
+#include "zfeature_common.h"
+
+/*
+ * If the device has being dynamically expanded then we need to relabel
+ * the disk to use the new unallocated space.
+ */
+int
+zpool_relabel_disk(libzfs_handle_t *hdl, const char *path, const char *msg)
+{
+ int fd, error;
+
+ if ((fd = open(path, O_RDWR|O_DIRECT)) < 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
+ "relabel '%s': unable to open device: %d"), path, errno);
+ return (zfs_error(hdl, EZFS_OPENFAILED, msg));
+ }
+
+ /*
+ * It's possible that we might encounter an error if the device
+ * does not have any unallocated space left. If so, we simply
+ * ignore that error and continue on.
+ *
+ * Also, we don't call efi_rescan() - that would just return EBUSY.
+ * The module will do it for us in vdev_disk_open().
+ */
+ error = efi_use_whole_disk(fd);
+
+ /* Flush the buffers to disk and invalidate the page cache. */
+ (void) fsync(fd);
+ (void) ioctl(fd, BLKFLSBUF);
+
+ (void) close(fd);
+ if (error && error != VT_ENOSPC) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
+ "relabel '%s': unable to read disk capacity"), path);
+ return (zfs_error(hdl, EZFS_NOCAP, msg));
+ }
+ return (0);
+}
+
+/*
+ * Read the EFI label from the config, if a label does not exist then
+ * pass back the error to the caller. If the caller has passed a non-NULL
+ * diskaddr argument then we set it to the starting address of the EFI
+ * partition.
+ */
+static int
+read_efi_label(nvlist_t *config, diskaddr_t *sb)
+{
+ char *path;
+ int fd;
+ char diskname[MAXPATHLEN];
+ int err = -1;
+
+ if (nvlist_lookup_string(config, ZPOOL_CONFIG_PATH, &path) != 0)
+ return (err);
+
+ (void) snprintf(diskname, sizeof (diskname), "%s%s", DISK_ROOT,
+ strrchr(path, '/'));
+ if ((fd = open(diskname, O_RDONLY|O_DIRECT)) >= 0) {
+ struct dk_gpt *vtoc;
+
+ if ((err = efi_alloc_and_read(fd, &vtoc)) >= 0) {
+ if (sb != NULL)
+ *sb = vtoc->efi_parts[0].p_start;
+ efi_free(vtoc);
+ }
+ (void) close(fd);
+ }
+ return (err);
+}
+
+/*
+ * determine where a partition starts on a disk in the current
+ * configuration
+ */
+static diskaddr_t
+find_start_block(nvlist_t *config)
+{
+ nvlist_t **child;
+ uint_t c, children;
+ diskaddr_t sb = MAXOFFSET_T;
+ uint64_t wholedisk;
+
+ if (nvlist_lookup_nvlist_array(config,
+ ZPOOL_CONFIG_CHILDREN, &child, &children) != 0) {
+ if (nvlist_lookup_uint64(config,
+ ZPOOL_CONFIG_WHOLE_DISK,
+ &wholedisk) != 0 || !wholedisk) {
+ return (MAXOFFSET_T);
+ }
+ if (read_efi_label(config, &sb) < 0)
+ sb = MAXOFFSET_T;
+ return (sb);
+ }
+
+ for (c = 0; c < children; c++) {
+ sb = find_start_block(child[c]);
+ if (sb != MAXOFFSET_T) {
+ return (sb);
+ }
+ }
+ return (MAXOFFSET_T);
+}
+
+static int
+zpool_label_disk_check(char *path)
+{
+ struct dk_gpt *vtoc;
+ int fd, err;
+
+ if ((fd = open(path, O_RDONLY|O_DIRECT)) < 0)
+ return (errno);
+
+ if ((err = efi_alloc_and_read(fd, &vtoc)) != 0) {
+ (void) close(fd);
+ return (err);
+ }
+
+ if (vtoc->efi_flags & EFI_GPT_PRIMARY_CORRUPT) {
+ efi_free(vtoc);
+ (void) close(fd);
+ return (EIDRM);
+ }
+
+ efi_free(vtoc);
+ (void) close(fd);
+ return (0);
+}
+
+/*
+ * Generate a unique partition name for the ZFS member. Partitions must
+ * have unique names to ensure udev will be able to create symlinks under
+ * /dev/disk/by-partlabel/ for all pool members. The partition names are
+ * of the form <pool>-<unique-id>.
+ */
+static void
+zpool_label_name(char *label_name, int label_size)
+{
+ uint64_t id = 0;
+ int fd;
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd >= 0) {
+ if (read(fd, &id, sizeof (id)) != sizeof (id))
+ id = 0;
+
+ close(fd);
+ }
+
+ if (id == 0)
+ id = (((uint64_t)rand()) << 32) | (uint64_t)rand();
+
+ snprintf(label_name, label_size, "zfs-%016llx", (u_longlong_t)id);
+}
+
+/*
+ * Label an individual disk. The name provided is the short name,
+ * stripped of any leading /dev path.
+ */
+int
+zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, const char *name)
+{
+ char path[MAXPATHLEN];
+ struct dk_gpt *vtoc;
+ int rval, fd;
+ size_t resv = EFI_MIN_RESV_SIZE;
+ uint64_t slice_size;
+ diskaddr_t start_block;
+ char errbuf[1024];
+
+ /* prepare an error message just in case */
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot label '%s'"), name);
+
+ if (zhp) {
+ nvlist_t *nvroot;
+
+ verify(nvlist_lookup_nvlist(zhp->zpool_config,
+ ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
+
+ if (zhp->zpool_start_block == 0)
+ start_block = find_start_block(nvroot);
+ else
+ start_block = zhp->zpool_start_block;
+ zhp->zpool_start_block = start_block;
+ } else {
+ /* new pool */
+ start_block = NEW_START_BLOCK;
+ }
+
+ (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name);
+
+ if ((fd = open(path, O_RDWR|O_DIRECT|O_EXCL)) < 0) {
+ /*
+ * This shouldn't happen. We've long since verified that this
+ * is a valid device.
+ */
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
+ "label '%s': unable to open device: %d"), path, errno);
+ return (zfs_error(hdl, EZFS_OPENFAILED, errbuf));
+ }
+
+ if (efi_alloc_and_init(fd, EFI_NUMPAR, &vtoc) != 0) {
+ /*
+ * The only way this can fail is if we run out of memory, or we
+ * were unable to read the disk's capacity
+ */
+ if (errno == ENOMEM)
+ (void) no_memory(hdl);
+
+ (void) close(fd);
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
+ "label '%s': unable to read disk capacity"), path);
+
+ return (zfs_error(hdl, EZFS_NOCAP, errbuf));
+ }
+
+ slice_size = vtoc->efi_last_u_lba + 1;
+ slice_size -= EFI_MIN_RESV_SIZE;
+ if (start_block == MAXOFFSET_T)
+ start_block = NEW_START_BLOCK;
+ slice_size -= start_block;
+ slice_size = P2ALIGN(slice_size, PARTITION_END_ALIGNMENT);
+
+ vtoc->efi_parts[0].p_start = start_block;
+ vtoc->efi_parts[0].p_size = slice_size;
+
+ /*
+ * Why we use V_USR: V_BACKUP confuses users, and is considered
+ * disposable by some EFI utilities (since EFI doesn't have a backup
+ * slice). V_UNASSIGNED is supposed to be used only for zero size
+ * partitions, and efi_write() will fail if we use it. V_ROOT, V_BOOT,
+ * etc. were all pretty specific. V_USR is as close to reality as we
+ * can get, in the absence of V_OTHER.
+ */
+ vtoc->efi_parts[0].p_tag = V_USR;
+ zpool_label_name(vtoc->efi_parts[0].p_name, EFI_PART_NAME_LEN);
+
+ vtoc->efi_parts[8].p_start = slice_size + start_block;
+ vtoc->efi_parts[8].p_size = resv;
+ vtoc->efi_parts[8].p_tag = V_RESERVED;
+
+ rval = efi_write(fd, vtoc);
+
+ /* Flush the buffers to disk and invalidate the page cache. */
+ (void) fsync(fd);
+ (void) ioctl(fd, BLKFLSBUF);
+
+ if (rval == 0)
+ rval = efi_rescan(fd);
+
+ /*
+ * Some block drivers (like pcata) may not support EFI GPT labels.
+ * Print out a helpful error message directing the user to manually
+ * label the disk and give a specific slice.
+ */
+ if (rval != 0) {
+ (void) close(fd);
+ efi_free(vtoc);
+
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "try using "
+ "parted(8) and then provide a specific slice: %d"), rval);
+ return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
+ }
+
+ (void) close(fd);
+ efi_free(vtoc);
+
+ (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name);
+ (void) zfs_append_partition(path, MAXPATHLEN);
+
+ /* Wait to udev to signal use the device has settled. */
+ rval = zpool_label_disk_wait(path, DISK_LABEL_WAIT);
+ if (rval) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "failed to "
+ "detect device partitions on '%s': %d"), path, rval);
+ return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
+ }
+
+ /* We can't be to paranoid. Read the label back and verify it. */
+ (void) snprintf(path, sizeof (path), "%s/%s", DISK_ROOT, name);
+ rval = zpool_label_disk_check(path);
+ if (rval) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "freshly written "
+ "EFI label on '%s' is damaged. Ensure\nthis device "
+ "is not in use, and is functioning properly: %d"),
+ path, rval);
+ return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
+ }
+ return (0);
+}
diff --git a/lib/libzfs/os/linux/libzfs_sendrecv_os.c b/lib/libzfs/os/linux/libzfs_sendrecv_os.c
new file mode 100644
index 0000000000000..eeb1f07f2dea3
--- /dev/null
+++ b/lib/libzfs/os/linux/libzfs_sendrecv_os.c
@@ -0,0 +1,52 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+
+#include <libzfs.h>
+
+#include "libzfs_impl.h"
+
+#ifndef F_SETPIPE_SZ
+#define F_SETPIPE_SZ (F_SETLEASE + 7)
+#endif /* F_SETPIPE_SZ */
+
+#ifndef F_GETPIPE_SZ
+#define F_GETPIPE_SZ (F_GETLEASE + 7)
+#endif /* F_GETPIPE_SZ */
+
+void
+libzfs_set_pipe_max(int infd)
+{
+ FILE *procf = fopen("/proc/sys/fs/pipe-max-size", "r");
+
+ if (procf != NULL) {
+ unsigned long max_psize;
+ long cur_psize;
+ if (fscanf(procf, "%lu", &max_psize) > 0) {
+ cur_psize = fcntl(infd, F_GETPIPE_SZ);
+ if (cur_psize > 0 &&
+ max_psize > (unsigned long) cur_psize)
+ fcntl(infd, F_SETPIPE_SZ,
+ max_psize);
+ }
+ fclose(procf);
+ }
+}
diff --git a/lib/libzfs/os/linux/libzfs_util_os.c b/lib/libzfs/os/linux/libzfs_util_os.c
new file mode 100644
index 0000000000000..918a43f7d03d9
--- /dev/null
+++ b/lib/libzfs/os/linux/libzfs_util_os.c
@@ -0,0 +1,215 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <math.h>
+#include <sys/stat.h>
+#include <sys/mnttab.h>
+#include <sys/mntent.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <libzfs.h>
+#include <libzfs_core.h>
+
+#include "libzfs_impl.h"
+#include "zfs_prop.h"
+#include <libzutil.h>
+#include <sys/zfs_sysfs.h>
+
+#define ZDIFF_SHARESDIR "/.zfs/shares/"
+
+int
+zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
+{
+ return (ioctl(hdl->libzfs_fd, request, zc));
+}
+
+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."));
+ 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."));
+ case ENOEXEC:
+ return (dgettext(TEXT_DOMAIN, "The ZFS modules cannot be "
+ "auto-loaded.\nTry running '/sbin/modprobe zfs' as "
+ "root to manually load them."));
+ case EACCES:
+ return (dgettext(TEXT_DOMAIN, "Permission denied the "
+ "ZFS utilities must be run as root."));
+ default:
+ return (dgettext(TEXT_DOMAIN, "Failed to initialize the "
+ "libzfs library."));
+ }
+}
+
+static int
+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);
+
+ return (access(path, F_OK) == 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_impl(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) {
+ 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);
+}
+
+int
+libzfs_load_module(void)
+{
+ return (libzfs_load_module_impl(ZFS_DRIVER));
+}
+
+int
+find_shares_object(differ_info_t *di)
+{
+ char fullpath[MAXPATHLEN];
+ struct stat64 sb = { 0 };
+
+ (void) strlcpy(fullpath, di->dsmnt, MAXPATHLEN);
+ (void) strlcat(fullpath, ZDIFF_SHARESDIR, MAXPATHLEN);
+
+ if (stat64(fullpath, &sb) != 0) {
+ (void) snprintf(di->errbuf, sizeof (di->errbuf),
+ dgettext(TEXT_DOMAIN, "Cannot stat %s"), fullpath);
+ return (zfs_error(di->zhp->zfs_hdl, EZFS_DIFF, di->errbuf));
+ }
+
+ di->shares = (uint64_t)sb.st_ino;
+ return (0);
+}
+
+/*
+ * Fill given version buffer with zfs kernel version read from ZFS_SYSFS_DIR
+ * Returns 0 on success, and -1 on error (with errno set)
+ */
+int
+zfs_version_kernel(char *version, int len)
+{
+ int _errno;
+ int fd;
+ int rlen;
+
+ if ((fd = open(ZFS_SYSFS_DIR "/version", O_RDONLY)) == -1)
+ return (-1);
+
+ if ((rlen = read(fd, version, len)) == -1) {
+ version[0] = '\0';
+ _errno = errno;
+ (void) close(fd);
+ errno = _errno;
+ return (-1);
+ }
+
+ version[rlen-1] = '\0'; /* discard '\n' */
+
+ if (close(fd) == -1)
+ return (-1);
+
+ return (0);
+}