aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Macy <[email protected]>2019-10-03 10:33:16 -0700
committerBrian Behlendorf <[email protected]>2019-10-03 10:33:16 -0700
commit73cdcc632304ed72aafabef1fb03c45a27dcc58f (patch)
treed525894fddc5da8fe0216f07903eb2f2009ceefd
parent7c5eff9400d12ed8f2676a6d08f6ae099351397f (diff)
OpenZFS restructuring - libzfs
Factor Linux specific functionality out of libzfs. Reviewed-by: Allan Jude <[email protected]> Reviewed-by: Jorgen Lundman <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Matthew Macy <[email protected]> Closes #9377
-rw-r--r--include/libzfs_impl.h46
-rw-r--r--lib/libzfs/Makefile.am18
-rw-r--r--lib/libzfs/libzfs_diff.c42
-rw-r--r--lib/libzfs/libzfs_mount.c321
-rw-r--r--lib/libzfs/libzfs_pool.c295
-rw-r--r--lib/libzfs/libzfs_sendrecv.c35
-rw-r--r--lib/libzfs/libzfs_util.c118
-rw-r--r--lib/libzfs/os/linux/libzfs_mount_os.c367
-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.c184
11 files changed, 1026 insertions, 797 deletions
diff --git a/include/libzfs_impl.h b/include/libzfs_impl.h
index 9a46b9f12..e9fd4cf41 100644
--- a/include/libzfs_impl.h
+++ b/include/libzfs_impl.h
@@ -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
diff --git a/lib/libzfs/Makefile.am b/lib/libzfs/Makefile.am
index 3d14a77c1..da42307bb 100644
--- a/lib/libzfs/Makefile.am
+++ b/lib/libzfs/Makefile.am
@@ -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
diff --git a/lib/libzfs/libzfs_diff.c b/lib/libzfs/libzfs_diff.c
index 1b5c44b04..169180751 100644
--- a/lib/libzfs/libzfs_diff.c
+++ b/lib/libzfs/libzfs_diff.c
@@ -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 '+'
@@ -56,26 +55,6 @@
#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
@@ -488,25 +467,6 @@ differ(void *arg)
}
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)
{
libzfs_handle_t *hdl = di->zhp->zfs_hdl;
@@ -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)
diff --git a/lib/libzfs/libzfs_mount.c b/lib/libzfs/libzfs_mount.c
index 5bd3c67be..c2955ccfa 100644
--- a/lib/libzfs/libzfs_mount.c
+++ b/lib/libzfs/libzfs_mount.c
@@ -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);
@@ -360,68 +295,6 @@ zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
*/
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;
@@ -821,44 +693,6 @@ zfs_is_shared_smb(zfs_handle_t *zhp, char **where)
}
/*
- * 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)
*
* Uninitialize the libshare API if it hasn't already been
@@ -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)
{
@@ -1001,50 +739,6 @@ zfs_shareall(zfs_handle_t *zhp)
}
/*
- * 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.
*/
int
@@ -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;
diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c
index 2641b1152..155f2c3ad 100644
--- a/lib/libzfs/libzfs_pool.c
+++ b/lib/libzfs/libzfs_pool.c
@@ -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 {
@@ -2815,45 +2813,6 @@ zpool_get_physpath(zpool_handle_t *zhp, char *physpath, size_t 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.
*
* If is_spare, is_l2cache, or is_log is non-NULL, then store within it
@@ -4433,260 +4392,6 @@ zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj,
}
/*
- * 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.
*/
int
diff --git a/lib/libzfs/libzfs_sendrecv.c b/lib/libzfs/libzfs_sendrecv.c
index 702643882..0b6b31568 100644
--- a/lib/libzfs/libzfs_sendrecv.c
+++ b/lib/libzfs/libzfs_sendrecv.c
@@ -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,
diff --git a/lib/libzfs/libzfs_util.c b/lib/libzfs/libzfs_util.c
index 04100071d..ae66db39a 100644
--- a/lib/libzfs/libzfs_util.c
+++ b/lib/libzfs/libzfs_util.c
@@ -56,6 +56,7 @@
#include <libzutil.h>
#include <sys/zfs_sysfs.h>
+
int
libzfs_errno(libzfs_handle_t *hdl)
{
@@ -63,31 +64,6 @@ libzfs_errno(libzfs_handle_t *hdl)
}
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)
{
return (hdl->libzfs_action);
@@ -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
index 000000000..af1cafd28
--- /dev/null
+++ b/lib/libzfs/os/linux/libzfs_mount_os.c
@@ -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 <[email protected]>
+ * 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
index 000000000..e1250f121
--- /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 <[email protected]>
+ * Copyright (c) 2018 Datto Inc.
+ * Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
+ * Copyright (c) 2017, Intel Corporation.
+ * Copyright (c) 2018, loli10K <[email protected]>
+ */
+
+#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
index 000000000..eeb1f07f2
--- /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 000000000..c27dc91bc
--- /dev/null
+++ b/lib/libzfs/os/linux/libzfs_util_os.c
@@ -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);
+}