]> granicus.if.org Git - zfs/commitdiff
OpenZFS restructuring - libzfs
authorMatthew Macy <mmacy@mattmacy.io>
Thu, 3 Oct 2019 17:33:16 +0000 (10:33 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Thu, 3 Oct 2019 17:33:16 +0000 (10:33 -0700)
Factor Linux specific functionality out of libzfs.

Reviewed-by: Allan Jude <allanjude@freebsd.org>
Reviewed-by: Jorgen Lundman <lundman@lundman.net>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Matthew Macy <mmacy@FreeBSD.org>
Closes #9377

include/libzfs_impl.h
lib/libzfs/Makefile.am
lib/libzfs/libzfs_diff.c
lib/libzfs/libzfs_mount.c
lib/libzfs/libzfs_pool.c
lib/libzfs/libzfs_sendrecv.c
lib/libzfs/libzfs_util.c
lib/libzfs/os/linux/libzfs_mount_os.c [new file with mode: 0644]
lib/libzfs/os/linux/libzfs_pool_os.c [new file with mode: 0644]
lib/libzfs/os/linux/libzfs_sendrecv_os.c [new file with mode: 0644]
lib/libzfs/os/linux/libzfs_util_os.c [new file with mode: 0644]

index 9a46b9f12960a16594b9b6034934a40fe937790c..e9fd4cf41fe7dc02c9bf24c88467a1b42d7ed7f3 100644 (file)
@@ -205,6 +205,52 @@ extern int zfs_parse_options(char *, zfs_share_proto_t);
 extern int zfs_unshare_proto(zfs_handle_t *,
     const char *, zfs_share_proto_t *);
 
+typedef struct {
+       zfs_prop_t p_prop;
+       char *p_name;
+       int p_share_err;
+       int p_unshare_err;
+} proto_table_t;
+
+typedef struct differ_info {
+       zfs_handle_t *zhp;
+       char *fromsnap;
+       char *frommnt;
+       char *tosnap;
+       char *tomnt;
+       char *ds;
+       char *dsmnt;
+       char *tmpsnap;
+       char errbuf[1024];
+       boolean_t isclone;
+       boolean_t scripted;
+       boolean_t classify;
+       boolean_t timestamped;
+       uint64_t shares;
+       int zerr;
+       int cleanupfd;
+       int outputfd;
+       int datafd;
+} differ_info_t;
+
+extern proto_table_t proto_table[PROTO_END];
+
+extern int do_mount(const char *src, const char *mntpt, char *opts, int flags);
+extern int do_unmount(const char *mntpt, int flags);
+extern int zfs_can_user_mount(void);
+extern int zfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto);
+extern int unshare_one(libzfs_handle_t *hdl, const char *name,
+    const char *mountpoint, zfs_share_proto_t proto);
+extern boolean_t zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
+    zprop_source_t *source, int flags);
+extern zfs_share_type_t is_shared_impl(libzfs_handle_t *hdl,
+    const char *mountpoint, zfs_share_proto_t proto);
+extern int libzfs_load_module(void);
+extern int zpool_relabel_disk(libzfs_handle_t *hdl, const char *path,
+    const char *msg);
+extern int find_shares_object(differ_info_t *di);
+extern void libzfs_set_pipe_max(int infd);
+
 #ifdef __cplusplus
 }
 #endif
index 3d14a77c1a8b78510881896f2b664f7bbef20fd2..da42307bb6fbeb69b67579e065bf1ef2700da395 100644 (file)
@@ -27,6 +27,15 @@ USER_C = \
        libzfs_status.c \
        libzfs_util.c
 
+if BUILD_LINUX
+USER_C += \
+       os/linux/libzfs_mount_os.c \
+       os/linux/libzfs_pool_os.c \
+       os/linux/libzfs_sendrecv_os.c \
+       os/linux/libzfs_util_os.c
+endif
+
+
 KERNEL_C = \
        algs/sha2/sha2.c \
        zfeature_common.c \
@@ -51,14 +60,19 @@ nodist_libzfs_la_SOURCES = \
 
 libzfs_la_LIBADD = \
        $(top_builddir)/lib/libnvpair/libnvpair.la \
-       $(top_builddir)/lib/libshare/libshare.la \
        $(top_builddir)/lib/libuutil/libuutil.la \
        $(top_builddir)/lib/libzfs_core/libzfs_core.la \
        $(top_builddir)/lib/libzutil/libzutil.la
 
-libzfs_la_LIBADD += -lm $(LIBSSL)
+if BUILD_LINUX
+libzfs_la_LIBADD += \
+       $(top_builddir)/lib/libshare/libshare.la
+endif
+
 libzfs_la_LDFLAGS = -version-info 2:0:0
 
+libzfs_la_LIBADD += -lm $(LIBSSL)
+
 EXTRA_DIST = $(libzfs_pc_DATA) $(USER_C)
 
 # Licensing data
index 1b5c44b047e2b154aa54e4cc740ffa6a3f2444ac..169180751d557937a2a1c816c99c53d7e96d14ff 100644 (file)
@@ -48,7 +48,6 @@
 #include "libzfs_impl.h"
 
 #define        ZDIFF_SNAPDIR           "/.zfs/snapshot/"
-#define        ZDIFF_SHARESDIR         "/.zfs/shares/"
 #define        ZDIFF_PREFIX            "zfs-diff-%d"
 
 #define        ZDIFF_ADDED     '+'
 #define        ZDIFF_REMOVED   '-'
 #define        ZDIFF_RENAMED   'R'
 
-typedef struct differ_info {
-       zfs_handle_t *zhp;
-       char *fromsnap;
-       char *frommnt;
-       char *tosnap;
-       char *tomnt;
-       char *ds;
-       char *dsmnt;
-       char *tmpsnap;
-       char errbuf[1024];
-       boolean_t isclone;
-       boolean_t scripted;
-       boolean_t classify;
-       boolean_t timestamped;
-       uint64_t shares;
-       int zerr;
-       int cleanupfd;
-       int outputfd;
-       int datafd;
-} differ_info_t;
 
 /*
  * Given a {dsname, object id}, get the object path
@@ -487,25 +466,6 @@ differ(void *arg)
        return ((void *)0);
 }
 
-static 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);
-}
-
 static int
 make_temp_snapshot(differ_info_t *di)
 {
@@ -737,7 +697,7 @@ setup_differ_info(zfs_handle_t *zhp, const char *fromsnap,
 {
        di->zhp = zhp;
 
-       di->cleanupfd = open(ZFS_DEV, O_RDWR);
+       di->cleanupfd = open(ZFS_DEV, O_RDWR|O_EXCL);
        VERIFY(di->cleanupfd >= 0);
 
        if (get_snapshot_names(di, fromsnap, tosnap) != 0)
index 5bd3c67bec5e082f09631343eae915156ae79329..c2955ccfaba51397915cc5000333d6b88f1d1d4a 100644 (file)
@@ -94,7 +94,6 @@
 static int mount_tp_nthr = 512;        /* tpool threads for multi-threaded mounting */
 
 static void zfs_mount_task(void *);
-static int zfs_share_proto(zfs_handle_t *, zfs_share_proto_t *);
 zfs_share_type_t zfs_is_shared_proto(zfs_handle_t *, char **,
     zfs_share_proto_t);
 
@@ -102,13 +101,6 @@ zfs_share_type_t zfs_is_shared_proto(zfs_handle_t *, char **,
  * The share protocols table must be in the same order as the zfs_share_proto_t
  * enum in libzfs_impl.h
  */
-typedef struct {
-       zfs_prop_t p_prop;
-       char *p_name;
-       int p_share_err;
-       int p_unshare_err;
-} proto_table_t;
-
 proto_table_t proto_table[PROTO_END] = {
        {ZFS_PROP_SHARENFS, "nfs", EZFS_SHARENFSFAILED, EZFS_UNSHARENFSFAILED},
        {ZFS_PROP_SHARESMB, "smb", EZFS_SHARESMBFAILED, EZFS_UNSHARESMBFAILED},
@@ -129,60 +121,7 @@ zfs_share_proto_t share_all_proto[] = {
        PROTO_END
 };
 
-/*
- * Search the sharetab for the given mountpoint and protocol, returning
- * a zfs_share_type_t value.
- */
-static zfs_share_type_t
-is_shared(libzfs_handle_t *hdl, const char *mountpoint, zfs_share_proto_t proto)
-{
-       char buf[MAXPATHLEN], *tab;
-       char *ptr;
-
-       if (hdl->libzfs_sharetab == NULL)
-               return (SHARED_NOT_SHARED);
-
-       /* Reopen ZFS_SHARETAB to prevent reading stale data from open file */
-       if (freopen(ZFS_SHARETAB, "r", hdl->libzfs_sharetab) == NULL)
-               return (SHARED_NOT_SHARED);
-
-       (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET);
-
-       while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) {
-
-               /* the mountpoint is the first entry on each line */
-               if ((tab = strchr(buf, '\t')) == NULL)
-                       continue;
-
-               *tab = '\0';
-               if (strcmp(buf, mountpoint) == 0) {
-                       /*
-                        * the protocol field is the third field
-                        * skip over second field
-                        */
-                       ptr = ++tab;
-                       if ((tab = strchr(ptr, '\t')) == NULL)
-                               continue;
-                       ptr = ++tab;
-                       if ((tab = strchr(ptr, '\t')) == NULL)
-                               continue;
-                       *tab = '\0';
-                       if (strcmp(ptr,
-                           proto_table[proto].p_name) == 0) {
-                               switch (proto) {
-                               case PROTO_NFS:
-                                       return (SHARED_NFS);
-                               case PROTO_SMB:
-                                       return (SHARED_SMB);
-                               default:
-                                       return (0);
-                               }
-                       }
-               }
-       }
 
-       return (SHARED_NOT_SHARED);
-}
 
 static boolean_t
 dir_is_empty_stat(const char *dirname)
@@ -304,7 +243,7 @@ zfs_is_mounted(zfs_handle_t *zhp, char **where)
  * Returns true if the given dataset is mountable, false otherwise.  Returns the
  * mountpoint in 'buf'.
  */
-static boolean_t
+boolean_t
 zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
     zprop_source_t *source, int flags)
 {
@@ -329,10 +268,6 @@ zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
            getzoneid() == GLOBAL_ZONEID)
                return (B_FALSE);
 
-       if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) &&
-           getzoneid() == GLOBAL_ZONEID)
-               return (B_FALSE);
-
        if (zfs_prop_get_int(zhp, ZFS_PROP_REDACTED) && !(flags & MS_FORCE))
                return (B_FALSE);
 
@@ -359,68 +294,6 @@ zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
  * http://www.kernel.org/pub/linux/utils/util-linux/libmount-docs/index.html
  */
 
-static int
-do_mount(const char *src, const char *mntpt, char *opts)
-{
-       char *argv[9] = {
-           "/bin/mount",
-           "--no-canonicalize",
-           "-t", MNTTYPE_ZFS,
-           "-o", opts,
-           (char *)src,
-           (char *)mntpt,
-           (char *)NULL };
-       int rc;
-
-       /* Return only the most critical mount error */
-       rc = libzfs_run_process(argv[0], argv, STDOUT_VERBOSE|STDERR_VERBOSE);
-       if (rc) {
-               if (rc & MOUNT_FILEIO)
-                       return (EIO);
-               if (rc & MOUNT_USER)
-                       return (EINTR);
-               if (rc & MOUNT_SOFTWARE)
-                       return (EPIPE);
-               if (rc & MOUNT_BUSY)
-                       return (EBUSY);
-               if (rc & MOUNT_SYSERR)
-                       return (EAGAIN);
-               if (rc & MOUNT_USAGE)
-                       return (EINVAL);
-
-               return (ENXIO); /* Generic error */
-       }
-
-       return (0);
-}
-
-static int
-do_unmount(const char *mntpt, int 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);
-}
-
 static int
 zfs_add_option(zfs_handle_t *zhp, char *options, int len,
     zfs_prop_t prop, char *on, char *off)
@@ -503,9 +376,8 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
                (void) strlcat(mntopts, "," MNTOPT_RO, sizeof (mntopts));
 
        if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL,
-           flags)) {
+           flags))
                return (0);
-       }
 
        /*
         * Append default mount options which apply to the mount point.
@@ -599,7 +471,7 @@ zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
        }
 
        /* perform the mount */
-       rc = do_mount(zfs_get_name(zhp), mountpoint, mntopts);
+       rc = do_mount(zfs_get_name(zhp), mountpoint, mntopts, flags);
        if (rc) {
                /*
                 * Generic errors are nasty, but there are just way too many
@@ -793,7 +665,7 @@ zfs_is_shared_proto(zfs_handle_t *zhp, char **where, zfs_share_proto_t proto)
        if (!zfs_is_mounted(zhp, &mountpoint))
                return (SHARED_NOT_SHARED);
 
-       if ((rc = is_shared(zhp->zfs_hdl, mountpoint, proto))
+       if ((rc = is_shared_impl(zhp->zfs_hdl, mountpoint, proto))
            != SHARED_NOT_SHARED) {
                if (where != NULL)
                        *where = mountpoint;
@@ -820,44 +692,6 @@ zfs_is_shared_smb(zfs_handle_t *zhp, char **where)
            PROTO_SMB) != SHARED_NOT_SHARED);
 }
 
-/*
- * zfs_init_libshare(zhandle, service)
- *
- * Initialize the libshare API if it hasn't already been initialized.
- * In all cases it returns 0 if it succeeded and an error if not. The
- * service value is which part(s) of the API to initialize and is a
- * direct map to the libshare sa_init(service) interface.
- */
-int
-zfs_init_libshare(libzfs_handle_t *zhandle, int service)
-{
-       int ret = SA_OK;
-
-       if (ret == SA_OK && zhandle->libzfs_shareflags & ZFSSHARE_MISS) {
-               /*
-                * We had a cache miss. Most likely it is a new ZFS
-                * dataset that was just created. We want to make sure
-                * so check timestamps to see if a different process
-                * has updated any of the configuration. If there was
-                * some non-ZFS change, we need to re-initialize the
-                * internal cache.
-                */
-               zhandle->libzfs_shareflags &= ~ZFSSHARE_MISS;
-               if (sa_needs_refresh(zhandle->libzfs_sharehdl)) {
-                       zfs_uninit_libshare(zhandle);
-                       zhandle->libzfs_sharehdl = sa_init(service);
-               }
-       }
-
-       if (ret == SA_OK && zhandle && zhandle->libzfs_sharehdl == NULL)
-               zhandle->libzfs_sharehdl = sa_init(service);
-
-       if (ret == SA_OK && zhandle->libzfs_sharehdl == NULL)
-               ret = SA_NO_MEMORY;
-
-       return (ret);
-}
-
 /*
  * zfs_uninit_libshare(zhandle)
  *
@@ -886,102 +720,6 @@ zfs_parse_options(char *options, zfs_share_proto_t proto)
            proto_table[proto].p_name));
 }
 
-/*
- * Share the given filesystem according to the options in the specified
- * protocol specific properties (sharenfs, sharesmb).  We rely
- * on "libshare" to do the dirty work for us.
- */
-static int
-zfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto)
-{
-       char mountpoint[ZFS_MAXPROPLEN];
-       char shareopts[ZFS_MAXPROPLEN];
-       char sourcestr[ZFS_MAXPROPLEN];
-       libzfs_handle_t *hdl = zhp->zfs_hdl;
-       sa_share_t share;
-       zfs_share_proto_t *curr_proto;
-       zprop_source_t sourcetype;
-       int ret;
-
-       if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL, 0))
-               return (0);
-
-       for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) {
-               /*
-                * Return success if there are no share options.
-                */
-               if (zfs_prop_get(zhp, proto_table[*curr_proto].p_prop,
-                   shareopts, sizeof (shareopts), &sourcetype, sourcestr,
-                   ZFS_MAXPROPLEN, B_FALSE) != 0 ||
-                   strcmp(shareopts, "off") == 0)
-                       continue;
-
-               ret = zfs_init_libshare(hdl, SA_INIT_SHARE_API);
-               if (ret != SA_OK) {
-                       (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
-                           dgettext(TEXT_DOMAIN, "cannot share '%s': %s"),
-                           zfs_get_name(zhp), sa_errorstr(ret));
-                       return (-1);
-               }
-
-               /*
-                * If the 'zoned' property is set, then zfs_is_mountable()
-                * will have already bailed out if we are in the global zone.
-                * But local zones cannot be NFS servers, so we ignore it for
-                * local zones as well.
-                */
-               if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED))
-                       continue;
-
-               share = sa_find_share(hdl->libzfs_sharehdl, mountpoint);
-               if (share == NULL) {
-                       /*
-                        * This may be a new file system that was just
-                        * created so isn't in the internal cache
-                        * (second time through). Rather than
-                        * reloading the entire configuration, we can
-                        * assume ZFS has done the checking and it is
-                        * safe to add this to the internal
-                        * configuration.
-                        */
-                       if (sa_zfs_process_share(hdl->libzfs_sharehdl,
-                           NULL, NULL, mountpoint,
-                           proto_table[*curr_proto].p_name, sourcetype,
-                           shareopts, sourcestr, zhp->zfs_name) != SA_OK) {
-                               (void) zfs_error_fmt(hdl,
-                                   proto_table[*curr_proto].p_share_err,
-                                   dgettext(TEXT_DOMAIN, "cannot share '%s'"),
-                                   zfs_get_name(zhp));
-                               return (-1);
-                       }
-                       hdl->libzfs_shareflags |= ZFSSHARE_MISS;
-                       share = sa_find_share(hdl->libzfs_sharehdl,
-                           mountpoint);
-               }
-               if (share != NULL) {
-                       int err;
-                       err = sa_enable_share(share,
-                           proto_table[*curr_proto].p_name);
-                       if (err != SA_OK) {
-                               (void) zfs_error_fmt(hdl,
-                                   proto_table[*curr_proto].p_share_err,
-                                   dgettext(TEXT_DOMAIN, "cannot share '%s'"),
-                                   zfs_get_name(zhp));
-                               return (-1);
-                       }
-               } else {
-                       (void) zfs_error_fmt(hdl,
-                           proto_table[*curr_proto].p_share_err,
-                           dgettext(TEXT_DOMAIN, "cannot share '%s'"),
-                           zfs_get_name(zhp));
-                       return (-1);
-               }
-
-       }
-       return (0);
-}
-
-
 int
 zfs_share_nfs(zfs_handle_t *zhp)
 {
@@ -1000,50 +738,6 @@ zfs_shareall(zfs_handle_t *zhp)
        return (zfs_share_proto(zhp, share_all_proto));
 }
 
-/*
- * Unshare a filesystem by mountpoint.
- */
-static int
-unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint,
-    zfs_share_proto_t proto)
-{
-       sa_share_t share;
-       int err;
-       char *mntpt;
-       /*
-        * Mountpoint could get trashed if libshare calls getmntany
-        * which it does during API initialization, so strdup the
-        * value.
-        */
-       mntpt = zfs_strdup(hdl, mountpoint);
-
-       /* make sure libshare initialized */
-       if ((err = zfs_init_libshare(hdl, SA_INIT_SHARE_API)) != SA_OK) {
-               free(mntpt);    /* don't need the copy anymore */
-               return (zfs_error_fmt(hdl, proto_table[proto].p_unshare_err,
-                   dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"),
-                   name, sa_errorstr(err)));
-       }
-
-       share = sa_find_share(hdl->libzfs_sharehdl, mntpt);
-       free(mntpt);    /* don't need the copy anymore */
-
-       if (share != NULL) {
-               err = sa_disable_share(share, proto_table[proto].p_name);
-               if (err != SA_OK) {
-                       return (zfs_error_fmt(hdl,
-                           proto_table[proto].p_unshare_err,
-                           dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"),
-                           name, sa_errorstr(err)));
-               }
-       } else {
-               return (zfs_error_fmt(hdl, proto_table[proto].p_unshare_err,
-                   dgettext(TEXT_DOMAIN, "cannot unshare '%s': not found"),
-                   name));
-       }
-       return (0);
-}
-
 /*
  * Unshare the given filesystem.
  */
@@ -1069,7 +763,7 @@ zfs_unshare_proto(zfs_handle_t *zhp, const char *mountpoint,
                for (curr_proto = proto; *curr_proto != PROTO_END;
                    curr_proto++) {
 
-                       if (is_shared(hdl, mntpt, *curr_proto) &&
+                       if (is_shared_impl(hdl, mntpt, *curr_proto) &&
                            unshare_one(hdl, zhp->zfs_name,
                            mntpt, *curr_proto) != 0) {
                                if (mntpt != NULL)
@@ -1170,7 +864,8 @@ remove_mountpoint(zfs_handle_t *zhp)
        char mountpoint[ZFS_MAXPROPLEN];
        zprop_source_t source;
 
-       if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), &source, 0))
+       if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint),
+           &source, 0))
                return;
 
        if (source == ZPROP_SRC_DEFAULT ||
@@ -1738,7 +1433,7 @@ zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force)
                zfs_share_proto_t *curr_proto;
                for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
                    curr_proto++) {
-                       if (is_shared(hdl, mountpoints[i], *curr_proto) &&
+                       if (is_shared_impl(hdl, mountpoints[i], *curr_proto) &&
                            unshare_one(hdl, mountpoints[i],
                            mountpoints[i], *curr_proto) != 0)
                                goto out;
index 2641b11528a224f2c21e2f42462ce798ddfd08ce..155f2c3adf88b6fbe32cefab54dfc794ef261103 100644 (file)
@@ -41,7 +41,6 @@
 #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>
@@ -53,7 +52,6 @@
 #include "zfs_comutil.h"
 #include "zfeature_common.h"
 
-static int read_efi_label(nvlist_t *config, diskaddr_t *sb);
 static boolean_t zpool_vdev_is_interior(const char *name);
 
 typedef struct prop_flags {
@@ -2814,45 +2812,6 @@ zpool_get_physpath(zpool_handle_t *zhp, char *physpath, size_t phypath_size)
            phypath_size));
 }
 
-/*
- * If the device has being dynamically expanded then we need to relabel
- * the disk to use the new unallocated space.
- */
-static 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);
-}
-
 /*
  * Convert a vdev path to a GUID.  Returns GUID or 0 on error.
  *
@@ -4432,260 +4391,6 @@ zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj,
        free(mntpnt);
 }
 
-/*
- * 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, 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);
-}
-
 /*
  * Wait while the specified activity is in progress in the pool.
  */
index 7026438828f40433ec6f3633e3c719fec485cda1..0b6b3156829bd21d2101b1caf4a8e2f2572c2b87 100644 (file)
@@ -2479,7 +2479,7 @@ zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
                ++holdseq;
                (void) snprintf(sdd.holdtag, sizeof (sdd.holdtag),
                    ".send-%d-%llu", getpid(), (u_longlong_t)holdseq);
-               sdd.cleanup_fd = open(ZFS_DEV, O_RDWR);
+               sdd.cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
                if (sdd.cleanup_fd < 0) {
                        err = errno;
                        goto stderr_out;
@@ -5390,37 +5390,12 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props,
                return (-2);
        }
 
-#ifdef __linux__
-#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 */
-
        /*
         * It is not uncommon for gigabytes to be processed in zfs receive.
-        * Speculatively increase the buffer size via Linux-specific fcntl()
-        * call.
+        * Speculatively increase the buffer size if supported by the platform.
         */
-       if (S_ISFIFO(sb.st_mode)) {
-               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)
-                                       (void) fcntl(infd, F_SETPIPE_SZ,
-                                           max_psize);
-                       }
-                       fclose(procf);
-               }
-       }
-#endif /* __linux__ */
+       if (S_ISFIFO(sb.st_mode))
+               libzfs_set_pipe_max(infd);
 
        if (props) {
                err = nvlist_lookup_string(props, "origin", &originsnap);
@@ -5428,7 +5403,7 @@ zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props,
                        return (err);
        }
 
-       cleanup_fd = open(ZFS_DEV, O_RDWR);
+       cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
        VERIFY(cleanup_fd >= 0);
 
        err = zfs_receive_impl(hdl, tosnap, originsnap, flags, infd, NULL, NULL,
index 04100071d051253a3c014fd88258ad2a86d545e2..ae66db39a4b6c64e428b3e0e2cbd5ff6c9d368a7 100644 (file)
 #include <libzutil.h>
 #include <sys/zfs_sysfs.h>
 
+
 int
 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."));
-       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."));
-       }
-}
-
 const char *
 libzfs_error_action(libzfs_handle_t *hdl)
 {
@@ -712,19 +688,6 @@ libzfs_print_on_error(libzfs_handle_t *hdl, boolean_t printerr)
        hdl->libzfs_printerr = printerr;
 }
 
-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);
-}
-
-
 /*
  * 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
@@ -903,84 +866,13 @@ libzfs_envvar_is_set(char *envvar)
        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 (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;
 
-       error = libzfs_load_module(ZFS_DRIVER);
+       error = libzfs_load_module();
        if (error) {
                errno = error;
                return (NULL);
@@ -1215,12 +1107,6 @@ zcmd_read_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t **nvlp)
        return (0);
 }
 
-int
-zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
-{
-       return (ioctl(hdl->libzfs_fd, request, zc));
-}
-
 /*
  * ================================================================
  * API shared by zfs and zpool property management
diff --git a/lib/libzfs/os/linux/libzfs_mount_os.c b/lib/libzfs/os/linux/libzfs_mount_os.c
new file mode 100644 (file)
index 0000000..af1cafd
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * 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, 2019 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>
+
+/*
+ * zfs_init_libshare(zhandle, service)
+ *
+ * Initialize the libshare API if it hasn't already been initialized.
+ * In all cases it returns 0 if it succeeded and an error if not. The
+ * service value is which part(s) of the API to initialize and is a
+ * direct map to the libshare sa_init(service) interface.
+ */
+int
+zfs_init_libshare(libzfs_handle_t *zhandle, int service)
+{
+       int ret = SA_OK;
+
+       if (ret == SA_OK && zhandle->libzfs_shareflags & ZFSSHARE_MISS) {
+               /*
+                * We had a cache miss. Most likely it is a new ZFS
+                * dataset that was just created. We want to make sure
+                * so check timestamps to see if a different process
+                * has updated any of the configuration. If there was
+                * some non-ZFS change, we need to re-initialize the
+                * internal cache.
+                */
+               zhandle->libzfs_shareflags &= ~ZFSSHARE_MISS;
+               if (sa_needs_refresh(zhandle->libzfs_sharehdl)) {
+                       zfs_uninit_libshare(zhandle);
+                       zhandle->libzfs_sharehdl = sa_init(service);
+               }
+       }
+
+       if (ret == SA_OK && zhandle && zhandle->libzfs_sharehdl == NULL)
+               zhandle->libzfs_sharehdl = sa_init(service);
+
+       if (ret == SA_OK && zhandle->libzfs_sharehdl == NULL)
+               ret = SA_NO_MEMORY;
+       return (ret);
+}
+
+
+/*
+ * Share the given filesystem according to the options in the specified
+ * protocol specific properties (sharenfs, sharesmb).  We rely
+ * on "libshare" to do the dirty work for us.
+ */
+int
+zfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto)
+{
+       char mountpoint[ZFS_MAXPROPLEN];
+       char shareopts[ZFS_MAXPROPLEN];
+       char sourcestr[ZFS_MAXPROPLEN];
+       libzfs_handle_t *hdl = zhp->zfs_hdl;
+       sa_share_t share;
+       zfs_share_proto_t *curr_proto;
+       zprop_source_t sourcetype;
+       int err, ret;
+
+       if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL, 0))
+               return (0);
+
+       for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) {
+               /*
+                * Return success if there are no share options.
+                */
+               if (zfs_prop_get(zhp, proto_table[*curr_proto].p_prop,
+                   shareopts, sizeof (shareopts), &sourcetype, sourcestr,
+                   ZFS_MAXPROPLEN, B_FALSE) != 0 ||
+                   strcmp(shareopts, "off") == 0)
+                       continue;
+
+               ret = zfs_init_libshare(hdl, SA_INIT_SHARE_API);
+               if (ret != SA_OK) {
+                       (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
+                           dgettext(TEXT_DOMAIN, "cannot share '%s': %s"),
+                           zfs_get_name(zhp), sa_errorstr(ret));
+                       return (-1);
+               }
+
+               /*
+                * If the 'zoned' property is set, then zfs_is_mountable()
+                * will have already bailed out if we are in the global zone.
+                * But local zones cannot be NFS servers, so we ignore it for
+                * local zones as well.
+                */
+               if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED))
+                       continue;
+
+               share = sa_find_share(hdl->libzfs_sharehdl, mountpoint);
+               if (share == NULL) {
+                       /*
+                        * This may be a new file system that was just
+                        * created so isn't in the internal cache
+                        * (second time through). Rather than
+                        * reloading the entire configuration, we can
+                        * assume ZFS has done the checking and it is
+                        * safe to add this to the internal
+                        * configuration.
+                        */
+                       if (sa_zfs_process_share(hdl->libzfs_sharehdl,
+                           NULL, NULL, mountpoint,
+                           proto_table[*curr_proto].p_name, sourcetype,
+                           shareopts, sourcestr, zhp->zfs_name) != SA_OK) {
+                               (void) zfs_error_fmt(hdl,
+                                   proto_table[*curr_proto].p_share_err,
+                                   dgettext(TEXT_DOMAIN, "cannot share '%s'"),
+                                   zfs_get_name(zhp));
+                               return (-1);
+                       }
+                       hdl->libzfs_shareflags |= ZFSSHARE_MISS;
+                       share = sa_find_share(hdl->libzfs_sharehdl,
+                           mountpoint);
+               }
+               if (share != NULL) {
+                       err = sa_enable_share(share,
+                           proto_table[*curr_proto].p_name);
+                       if (err != SA_OK) {
+                               (void) zfs_error_fmt(hdl,
+                                   proto_table[*curr_proto].p_share_err,
+                                   dgettext(TEXT_DOMAIN, "cannot share '%s'"),
+                                   zfs_get_name(zhp));
+                               return (-1);
+                       }
+               } else {
+                       (void) zfs_error_fmt(hdl,
+                           proto_table[*curr_proto].p_share_err,
+                           dgettext(TEXT_DOMAIN, "cannot share '%s'"),
+                           zfs_get_name(zhp));
+                       return (-1);
+               }
+
+       }
+       return (0);
+}
+
+/*
+ * Unshare a filesystem by mountpoint.
+ */
+int
+unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint,
+    zfs_share_proto_t proto)
+{
+       sa_share_t share;
+       int err;
+       char *mntpt;
+       /*
+        * Mountpoint could get trashed if libshare calls getmntany
+        * which it does during API initialization, so strdup the
+        * value.
+        */
+       mntpt = zfs_strdup(hdl, mountpoint);
+
+       /* make sure libshare initialized */
+       if ((err = zfs_init_libshare(hdl, SA_INIT_SHARE_API)) != SA_OK) {
+               free(mntpt);    /* don't need the copy anymore */
+               return (zfs_error_fmt(hdl, proto_table[proto].p_unshare_err,
+                   dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"),
+                   name, sa_errorstr(err)));
+       }
+
+       share = sa_find_share(hdl->libzfs_sharehdl, mntpt);
+       free(mntpt);    /* don't need the copy anymore */
+
+       if (share != NULL) {
+               err = sa_disable_share(share, proto_table[proto].p_name);
+               if (err != SA_OK) {
+                       return (zfs_error_fmt(hdl,
+                           proto_table[proto].p_unshare_err,
+                           dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"),
+                           name, sa_errorstr(err)));
+               }
+       } else {
+               return (zfs_error_fmt(hdl, proto_table[proto].p_unshare_err,
+                   dgettext(TEXT_DOMAIN, "cannot unshare '%s': not found"),
+                   name));
+       }
+       return (0);
+}
+
+/*
+ * Search the sharetab for the given mountpoint and protocol, returning
+ * a zfs_share_type_t value.
+ */
+zfs_share_type_t
+is_shared_impl(libzfs_handle_t *hdl, const char *mountpoint,
+    zfs_share_proto_t proto)
+{
+       char buf[MAXPATHLEN], *tab;
+       char *ptr;
+
+       if (hdl->libzfs_sharetab == NULL)
+               return (SHARED_NOT_SHARED);
+
+       /* Reopen ZFS_SHARETAB to prevent reading stale data from open file */
+       if (freopen(ZFS_SHARETAB, "r", hdl->libzfs_sharetab) == NULL)
+               return (SHARED_NOT_SHARED);
+
+       (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET);
+
+       while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) {
+
+               /* the mountpoint is the first entry on each line */
+               if ((tab = strchr(buf, '\t')) == NULL)
+                       continue;
+
+               *tab = '\0';
+               if (strcmp(buf, mountpoint) == 0) {
+                       /*
+                        * the protocol field is the third field
+                        * skip over second field
+                        */
+                       ptr = ++tab;
+                       if ((tab = strchr(ptr, '\t')) == NULL)
+                               continue;
+                       ptr = ++tab;
+                       if ((tab = strchr(ptr, '\t')) == NULL)
+                               continue;
+                       *tab = '\0';
+                       if (strcmp(ptr,
+                           proto_table[proto].p_name) == 0) {
+                               switch (proto) {
+                               case PROTO_NFS:
+                                       return (SHARED_NFS);
+                               case PROTO_SMB:
+                                       return (SHARED_SMB);
+                               default:
+                                       return (0);
+                               }
+                       }
+               }
+       }
+
+       return (SHARED_NOT_SHARED);
+}
+
+/*
+ * 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.
+ *
+ * In the long term a shared library called libmount is under development
+ * which provides a common API to address the locking and errno issues.
+ * Once the standard mount utility has been updated to use this library
+ * we can add an autoconf check to conditionally use it.
+ *
+ * http://www.kernel.org/pub/linux/utils/util-linux/libmount-docs/index.html
+ */
+int
+do_mount(const char *src, const char *mntpt, char *opts, int flags)
+{
+       char *argv[9] = {
+           "/bin/mount",
+           "--no-canonicalize",
+           "-t", MNTTYPE_ZFS,
+           "-o", opts,
+           (char *)src,
+           (char *)mntpt,
+           (char *)NULL };
+       int rc;
+
+       /* Return only the most critical mount error */
+       rc = libzfs_run_process(argv[0], argv, STDOUT_VERBOSE|STDERR_VERBOSE);
+       if (rc) {
+               if (rc & MOUNT_FILEIO)
+                       return (EIO);
+               if (rc & MOUNT_USER)
+                       return (EINTR);
+               if (rc & MOUNT_SOFTWARE)
+                       return (EPIPE);
+               if (rc & MOUNT_BUSY)
+                       return (EBUSY);
+               if (rc & MOUNT_SYSERR)
+                       return (EAGAIN);
+               if (rc & MOUNT_USAGE)
+                       return (EINVAL);
+
+               return (ENXIO); /* Generic error */
+       }
+
+       return (0);
+}
+
+int
+do_unmount(const char *mntpt, int 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_can_user_mount(void)
+{
+       return (geteuid() == 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 (file)
index 0000000..e1250f1
--- /dev/null
@@ -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, 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 (file)
index 0000000..eeb1f07
--- /dev/null
@@ -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 (file)
index 0000000..c27dc91
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * 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>
+
+#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);
+}