aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/zfs/zfs_main.c8
-rw-r--r--cmd/zpool/zpool_main.c6
-rw-r--r--etc/systemd/system/zfs-share.service.in1
-rw-r--r--include/libzfs.h4
-rw-r--r--include/libzfs_impl.h16
-rw-r--r--include/sys/fs/zfs.h1
-rw-r--r--lib/libshare/Makefile.am16
-rw-r--r--lib/libshare/libshare.c541
-rw-r--r--lib/libshare/libshare_impl.h27
-rw-r--r--lib/libshare/os/freebsd/nfs.c448
-rw-r--r--lib/libshare/os/freebsd/smb.c128
-rw-r--r--lib/libshare/os/linux/nfs.c (renamed from lib/libshare/nfs.c)640
-rw-r--r--lib/libshare/os/linux/smb.c (renamed from lib/libshare/smb.c)56
-rw-r--r--lib/libspl/include/libshare.h24
-rw-r--r--lib/libzfs/Makefile.am11
-rw-r--r--lib/libzfs/libzfs_changelist.c41
-rw-r--r--lib/libzfs/libzfs_dataset.c38
-rw-r--r--lib/libzfs/libzfs_mount.c168
-rw-r--r--lib/libzfs/libzfs_util.c9
-rw-r--r--lib/libzfs/os/freebsd/libzfs_fsshare.c406
-rw-r--r--lib/libzfs/os/linux/libzfs_mount_os.c235
-rw-r--r--tests/runfiles/common.run5
-rw-r--r--tests/runfiles/linux.run4
-rwxr-xr-xtests/test-runner/bin/zts-report.py3
-rw-r--r--tests/test-runner/include/logapi.shlib16
-rw-r--r--tests/zfs-tests/include/libtest.shlib122
-rw-r--r--tests/zfs-tests/tests/functional/cli_root/zfs_share/Makefile.am3
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_share/setup.ksh5
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_share/zfs_share_001_pos.ksh13
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_share/zfs_share_concurrent_shares.ksh201
30 files changed, 1545 insertions, 1651 deletions
diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index 224ed1604..c6a29095e 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -772,6 +772,7 @@ zfs_mount_and_share(libzfs_handle_t *hdl, const char *dataset, zfs_type_t type)
"successfully created, but not shared\n"));
ret = 1;
}
+ zfs_commit_all_shares();
}
zfs_close(zhp);
@@ -6927,6 +6928,8 @@ share_mount(int op, int argc, char **argv)
zfs_foreach_mountpoint(g_zfs, cb.cb_handles, cb.cb_used,
share_mount_one_cb, &share_mount_state,
op == OP_MOUNT && !(flags & MS_CRYPT));
+ zfs_commit_all_shares();
+
ret = share_mount_state.sm_status;
for (int i = 0; i < cb.cb_used; i++)
@@ -6979,6 +6982,7 @@ share_mount(int op, int argc, char **argv)
} else {
ret = share_mount_one(zhp, op, flags, NULL, B_TRUE,
options);
+ zfs_commit_all_shares();
zfs_close(zhp);
}
}
@@ -7108,6 +7112,7 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
"not currently shared\n"), path);
} else {
ret = zfs_unshareall_bypath(zhp, path);
+ zfs_commit_all_shares();
}
} else {
char mtpt_prop[ZFS_MAXPROPLEN];
@@ -7328,6 +7333,9 @@ unshare_unmount(int op, int argc, char **argv)
free(node);
}
+ if (op == OP_SHARE)
+ zfs_commit_shares(protocol);
+
uu_avl_walk_end(walk);
uu_avl_destroy(tree);
uu_avl_pool_destroy(pool);
diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c
index cdf5511fe..de689afa7 100644
--- a/cmd/zpool/zpool_main.c
+++ b/cmd/zpool/zpool_main.c
@@ -22,7 +22,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2011, 2018 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
* Copyright (c) 2012 by Frederik Wessels. All rights reserved.
* Copyright (c) 2012 by Cyril Plisko. All rights reserved.
* Copyright (c) 2013 by Prasad Joshi (sTec). All rights reserved.
@@ -1587,8 +1587,10 @@ zpool_do_create(int argc, char **argv)
zfs_handle_t *pool = zfs_open(g_zfs,
tname ? tname : poolname, ZFS_TYPE_FILESYSTEM);
if (pool != NULL) {
- if (zfs_mount(pool, NULL, 0) == 0)
+ if (zfs_mount(pool, NULL, 0) == 0) {
ret = zfs_shareall(pool);
+ zfs_commit_all_shares();
+ }
zfs_close(pool);
}
} else if (libzfs_errno(g_zfs) == EZFS_INVALIDNAME) {
diff --git a/etc/systemd/system/zfs-share.service.in b/etc/systemd/system/zfs-share.service.in
index 5f4ba411b..b72008587 100644
--- a/etc/systemd/system/zfs-share.service.in
+++ b/etc/systemd/system/zfs-share.service.in
@@ -12,7 +12,6 @@ PartOf=smb.service
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStartPre=-/bin/rm -f /etc/dfs/sharetab
ExecStart=@sbindir@/zfs share -a
[Install]
diff --git a/include/libzfs.h b/include/libzfs.h
index a93f694a1..6f797c0a3 100644
--- a/include/libzfs.h
+++ b/include/libzfs.h
@@ -837,6 +837,10 @@ extern int zfs_unshareall_bytype(zfs_handle_t *, const char *, const char *);
extern int zfs_unshareall(zfs_handle_t *);
extern int zfs_deleg_share_nfs(libzfs_handle_t *, char *, char *, char *,
void *, void *, int, zfs_share_op_t);
+extern void zfs_commit_nfs_shares(void);
+extern void zfs_commit_smb_shares(void);
+extern void zfs_commit_all_shares(void);
+extern void zfs_commit_shares(const char *);
extern int zfs_nicestrtonum(libzfs_handle_t *, const char *, uint64_t *);
diff --git a/include/libzfs_impl.h b/include/libzfs_impl.h
index 35b92f6d9..0f1116797 100644
--- a/include/libzfs_impl.h
+++ b/include/libzfs_impl.h
@@ -49,7 +49,6 @@ struct libzfs_handle {
int libzfs_error;
int libzfs_fd;
FILE *libzfs_mnttab;
- FILE *libzfs_sharetab;
zpool_handle_t *libzfs_pool_handles;
uu_avl_pool_t *libzfs_ns_avlpool;
uu_avl_t *libzfs_ns_avl;
@@ -59,8 +58,6 @@ struct libzfs_handle {
char libzfs_desc[1024];
int libzfs_printerr;
int libzfs_storeerr; /* stuff error messages into buffer */
- void *libzfs_sharehdl; /* libshare handle */
- uint_t libzfs_shareflags;
boolean_t libzfs_mnttab_enable;
/*
* We need a lock to handle the case where parallel mount
@@ -76,8 +73,6 @@ struct libzfs_handle {
regex_t libzfs_urire;
};
-#define ZFSSHARE_MISS 0x01 /* Didn't find entry in cache */
-
struct zfs_handle {
libzfs_handle_t *zfs_hdl;
zpool_handle_t *zpool_hdl;
@@ -206,12 +201,6 @@ int zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
void namespace_clear(libzfs_handle_t *);
-/*
- * libshare (sharemgr) interfaces used internally.
- */
-
-extern int zfs_init_libshare(libzfs_handle_t *, int);
-extern void zfs_uninit_libshare(libzfs_handle_t *);
extern int zfs_parse_options(char *, zfs_share_proto_t);
extern int zfs_unshare_proto(zfs_handle_t *,
@@ -256,13 +245,14 @@ 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 zfs_share_type_t is_shared(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);
+extern void zfs_commit_proto(zfs_share_proto_t *);
#ifdef __cplusplus
}
diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h
index 1bfd7a485..ef87c24a4 100644
--- a/include/sys/fs/zfs.h
+++ b/include/sys/fs/zfs.h
@@ -1170,7 +1170,6 @@ typedef struct ddt_histogram {
#define ZVOL_DRIVER "zvol"
#define ZFS_DRIVER "zfs"
#define ZFS_DEV "/dev/zfs"
-#define ZFS_SHARETAB "/etc/dfs/sharetab"
#define ZFS_SUPER_MAGIC 0x2fc12fc1
diff --git a/lib/libshare/Makefile.am b/lib/libshare/Makefile.am
index 1504644c0..e730ee3be 100644
--- a/lib/libshare/Makefile.am
+++ b/lib/libshare/Makefile.am
@@ -1,13 +1,25 @@
include $(top_srcdir)/config/Rules.am
+DEFAULT_INCLUDES += -I$(srcdir)
+
noinst_LTLIBRARIES = libshare.la
USER_C = \
libshare_impl.h \
libshare.c \
- nfs.c \
nfs.h \
- smb.c \
smb.h
+if BUILD_LINUX
+USER_C += \
+ os/linux/nfs.c \
+ os/linux/smb.c
+endif
+
+if BUILD_FREEBSD
+USER_C += \
+ os/freebsd/nfs.c \
+ os/freebsd/smb.c
+endif
+
libshare_la_SOURCES = $(USER_C)
diff --git a/lib/libshare/libshare.c b/lib/libshare/libshare.c
index 36074641d..d32a282a3 100644
--- a/lib/libshare/libshare.c
+++ b/lib/libshare/libshare.c
@@ -22,6 +22,7 @@
/*
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011 Gunnar Beutner
+ * Copyright (c) 2018, 2020 by Delphix. All rights reserved.
*/
#include <stdio.h>
@@ -29,30 +30,20 @@
#include <errno.h>
#include <strings.h>
#include <libintl.h>
+#include <sys/file.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <libzfs.h>
#include <libshare.h>
+#include "libzfs_impl.h"
#include "libshare_impl.h"
#include "nfs.h"
#include "smb.h"
-static sa_share_impl_t find_share(sa_handle_impl_t handle,
- const char *sharepath);
-static sa_share_impl_t alloc_share(const char *sharepath);
+static sa_share_impl_t alloc_share(const char *zfsname, const char *path);
static void free_share(sa_share_impl_t share);
-static void parse_sharetab(sa_handle_impl_t impl_handle);
-static int process_share(sa_handle_impl_t impl_handle,
- sa_share_impl_t impl_share, char *pathname, char *resource,
- char *fstype, char *options, char *description,
- char *dataset, boolean_t from_sharetab);
-static void update_sharetab(sa_handle_impl_t impl_handle);
-
-static int update_zfs_share(sa_share_impl_t impl_handle, const char *proto);
-static int update_zfs_shares(sa_handle_impl_t impl_handle, const char *proto);
-
static int fstypes_count;
static sa_fstype_t *fstypes;
@@ -78,28 +69,6 @@ register_fstype(const char *name, const sa_share_ops_t *ops)
return (fstype);
}
-sa_handle_t
-sa_init(int init_service)
-{
- sa_handle_impl_t impl_handle;
-
- impl_handle = calloc(1, sizeof (struct sa_handle_impl));
-
- if (impl_handle == NULL)
- return (NULL);
-
- impl_handle->zfs_libhandle = libzfs_init();
-
- if (impl_handle->zfs_libhandle != NULL) {
- libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE);
- }
-
- parse_sharetab(impl_handle);
- update_zfs_shares(impl_handle, NULL);
-
- return ((sa_handle_t)impl_handle);
-}
-
__attribute__((constructor)) static void
libshare_init(void)
{
@@ -107,448 +76,101 @@ libshare_init(void)
libshare_smb_init();
}
-static void
-parse_sharetab(sa_handle_impl_t impl_handle)
-{
- FILE *fp;
- char line[512];
- char *eol, *pathname, *resource, *fstype, *options, *description;
-
- fp = fopen(ZFS_SHARETAB, "r");
-
- if (fp == NULL)
- return;
-
- while (fgets(line, sizeof (line), fp) != NULL) {
- eol = line + strlen(line) - 1;
-
- while (eol >= line) {
- if (*eol != '\r' && *eol != '\n')
- break;
-
- *eol = '\0';
- eol--;
- }
-
- pathname = line;
-
- if ((resource = strchr(pathname, '\t')) == NULL)
- continue;
-
- *resource = '\0';
- resource++;
-
- if ((fstype = strchr(resource, '\t')) == NULL)
- continue;
-
- *fstype = '\0';
- fstype++;
-
- if ((options = strchr(fstype, '\t')) == NULL)
- continue;
-
- *options = '\0';
- options++;
-
- if ((description = strchr(fstype, '\t')) != NULL) {
- *description = '\0';
- description++;
- }
-
- if (strcmp(resource, "-") == 0)
- resource = NULL;
-
- (void) process_share(impl_handle, NULL, pathname, resource,
- fstype, options, description, NULL, B_TRUE);
- }
-
- fclose(fp);
-}
-
-static void
-update_sharetab(sa_handle_impl_t impl_handle)
-{
- sa_share_impl_t impl_share;
- int temp_fd;
- FILE *temp_fp;
- char tempfile[] = ZFS_SHARETAB".XXXXXX";
- sa_fstype_t *fstype;
- const char *resource;
-
- if (mkdir("/etc/dfs", 0755) < 0 && errno != EEXIST) {
- return;
- }
-
- temp_fd = mkstemp(tempfile);
-
- if (temp_fd < 0)
- return;
-
- temp_fp = fdopen(temp_fd, "w");
-
- if (temp_fp == NULL)
- return;
-
- impl_share = impl_handle->shares;
- while (impl_share != NULL) {
- fstype = fstypes;
- while (fstype != NULL) {
- if (FSINFO(impl_share, fstype)->active &&
- FSINFO(impl_share, fstype)->shareopts != NULL) {
- resource = FSINFO(impl_share, fstype)->resource;
-
- if (resource == NULL)
- resource = "-";
-
- fprintf(temp_fp, "%s\t%s\t%s\t%s\n",
- impl_share->sharepath, resource,
- fstype->name,
- FSINFO(impl_share, fstype)->shareopts);
- }
-
- fstype = fstype->next;
- }
-
- impl_share = impl_share->next;
- }
-
- fflush(temp_fp);
- fsync(temp_fd);
- fclose(temp_fp);
-
- (void) rename(tempfile, ZFS_SHARETAB);
-}
-
-typedef struct update_cookie_s {
- sa_handle_impl_t handle;
- const char *proto;
-} update_cookie_t;
-
-static int
-update_zfs_shares_cb(zfs_handle_t *zhp, void *pcookie)
-{
- update_cookie_t *udata = (update_cookie_t *)pcookie;
- char mountpoint[ZFS_MAXPROPLEN];
- char shareopts[ZFS_MAXPROPLEN];
- char *dataset;
- zfs_type_t type = zfs_get_type(zhp);
-
- if (type == ZFS_TYPE_FILESYSTEM &&
- zfs_iter_filesystems(zhp, update_zfs_shares_cb, pcookie) != 0) {
- zfs_close(zhp);
- return (1);
- }
-
- if (type != ZFS_TYPE_FILESYSTEM) {
- zfs_close(zhp);
- return (0);
- }
-
- if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
- sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
- zfs_close(zhp);
- return (0);
- }
-
- dataset = (char *)zfs_get_name(zhp);
-
- if (dataset == NULL) {
- zfs_close(zhp);
- return (0);
- }
-
- if (!zfs_is_mounted(zhp, NULL)) {
- zfs_close(zhp);
- return (0);
- }
-
- if ((udata->proto == NULL || strcmp(udata->proto, "nfs") == 0) &&
- zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,
- sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0 &&
- strcmp(shareopts, "off") != 0) {
- (void) process_share(udata->handle, NULL, mountpoint, NULL,
- "nfs", shareopts, NULL, dataset, B_FALSE);
- }
-
- if ((udata->proto == NULL || strcmp(udata->proto, "smb") == 0) &&
- zfs_prop_get(zhp, ZFS_PROP_SHARESMB, shareopts,
- sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0 &&
- strcmp(shareopts, "off") != 0) {
- (void) process_share(udata->handle, NULL, mountpoint, NULL,
- "smb", shareopts, NULL, dataset, B_FALSE);
- }
-
- zfs_close(zhp);
-
- return (0);
-}
-
-static int
-update_zfs_share(sa_share_impl_t impl_share, const char *proto)
-{
- sa_handle_impl_t impl_handle = impl_share->handle;
- zfs_handle_t *zhp;
- update_cookie_t udata;
-
- if (impl_handle->zfs_libhandle == NULL)
- return (SA_SYSTEM_ERR);
-
- assert(impl_share->dataset != NULL);
-
- zhp = zfs_open(impl_share->handle->zfs_libhandle, impl_share->dataset,
- ZFS_TYPE_FILESYSTEM);
-
- if (zhp == NULL)
- return (SA_SYSTEM_ERR);
-
- udata.handle = impl_handle;
- udata.proto = proto;
- (void) update_zfs_shares_cb(zhp, &udata);
-
- return (SA_OK);
-}
-
-static int
-update_zfs_shares(sa_handle_impl_t impl_handle, const char *proto)
-{
- update_cookie_t udata;
-
- if (impl_handle->zfs_libhandle == NULL)
- return (SA_SYSTEM_ERR);
-
- udata.handle = impl_handle;
- udata.proto = proto;
- (void) zfs_iter_root(impl_handle->zfs_libhandle, update_zfs_shares_cb,
- &udata);
-
- return (SA_OK);
-}
-
-static int
-process_share(sa_handle_impl_t impl_handle, sa_share_impl_t impl_share,
- char *pathname, char *resource, char *proto,
- char *options, char *description, char *dataset,
- boolean_t from_sharetab)
+int
+sa_enable_share(const char *zfsname, const char *mountpoint,
+ const char *shareopts, char *protocol)
{
- struct stat statbuf;
- int rc;
- char *resource_dup = NULL, *dataset_dup = NULL;
- boolean_t new_share;
+ int rc, ret = SA_OK;
+ boolean_t found_protocol = B_FALSE;
sa_fstype_t *fstype;
- new_share = B_FALSE;
-
+ sa_share_impl_t impl_share = alloc_share(zfsname, mountpoint);
if (impl_share == NULL)
- impl_share = find_share(impl_handle, pathname);
-
- if (impl_share == NULL) {
- if (lstat(pathname, &statbuf) != 0 ||
- !S_ISDIR(statbuf.st_mode))
- return (SA_BAD_PATH);
-
- impl_share = alloc_share(pathname);
-
- if (impl_share == NULL) {
- rc = SA_NO_MEMORY;
- goto err;
- }
-
- new_share = B_TRUE;
- }
-
- if (dataset != NULL) {
- dataset_dup = strdup(dataset);
-
- if (dataset_dup == NULL) {
- rc = SA_NO_MEMORY;
- goto err;
- }
- }
-
- free(impl_share->dataset);
- impl_share->dataset = dataset_dup;
-
- rc = SA_INVALID_PROTOCOL;
+ return (SA_NO_MEMORY);
fstype = fstypes;
while (fstype != NULL) {
- if (strcmp(fstype->name, proto) == 0) {
- if (resource != NULL) {
- resource_dup = strdup(resource);
-
- if (resource_dup == NULL) {
- rc = SA_NO_MEMORY;
- goto err;
- }
- }
-
- free(FSINFO(impl_share, fstype)->resource);
- FSINFO(impl_share, fstype)->resource = resource_dup;
+ if (strcmp(fstype->name, protocol) == 0) {
rc = fstype->ops->update_shareopts(impl_share,
- resource, options);
+ shareopts);
+ if (rc != SA_OK)
+ break;
- if (rc == SA_OK && from_sharetab)
- FSINFO(impl_share, fstype)->active = B_TRUE;
+ rc = fstype->ops->enable_share(impl_share);
+ if (rc != SA_OK)
+ ret = rc;
- break;
+ found_protocol = B_TRUE;
}
fstype = fstype->next;
}
+ free_share(impl_share);
- if (rc != SA_OK)
- goto err;
-
- if (new_share) {
- impl_share->handle = impl_handle;
-
- impl_share->next = impl_handle->shares;
- impl_handle->shares = impl_share;
-
- }
-
-err:
- if (rc != SA_OK) {
- if (new_share)
- free_share(impl_share);
- }
-
- return (rc);
-}
-
-void
-sa_fini(sa_handle_t handle)
-{
- sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
- sa_share_impl_t impl_share, next;
- sa_share_impl_t *pcurr;
-
- if (impl_handle == NULL)
- return;
-
- /*
- * clean up shares which don't have a non-NULL dataset property,
- * which means they're in sharetab but we couldn't find their
- * ZFS dataset.
- */
- pcurr = &(impl_handle->shares);
- impl_share = *pcurr;
- while (impl_share != NULL) {
- next = impl_share->next;
-
- if (impl_share->dataset == NULL) {
- /* remove item from the linked list */
- *pcurr = next;
-
- sa_disable_share(impl_share, NULL);
-
- free_share(impl_share);
- } else {
- pcurr = &(impl_share->next);
- }
-
- impl_share = next;
- }
-
- update_sharetab(impl_handle);
-
- if (impl_handle->zfs_libhandle != NULL)
- libzfs_fini(impl_handle->zfs_libhandle);
-
- impl_share = impl_handle->shares;
- while (impl_share != NULL) {
- next = impl_share->next;
- free_share(impl_share);
- impl_share = next;
- }
-
- free(impl_handle);
-}
-
-static sa_share_impl_t
-find_share(sa_handle_impl_t impl_handle, const char *sharepath)
-{
- sa_share_impl_t impl_share;
-
- impl_share = impl_handle->shares;
- while (impl_share != NULL) {
- if (strcmp(impl_share->sharepath, sharepath) == 0) {
- break;
- }
-
- impl_share = impl_share->next;
- }
-
- return (impl_share);
-}
-
-sa_share_t
-sa_find_share(sa_handle_t handle, char *sharepath)
-{
- return ((sa_share_t)find_share((sa_handle_impl_t)handle, sharepath));
+ return (found_protocol ? ret : SA_INVALID_PROTOCOL);
}
int
-sa_enable_share(sa_share_t share, char *protocol)
+sa_disable_share(const char *mountpoint, char *protocol)
{
- sa_share_impl_t impl_share = (sa_share_impl_t)share;
int rc, ret = SA_OK;
boolean_t found_protocol = B_FALSE;
sa_fstype_t *fstype;
+ sa_share_impl_t impl_share = alloc_share(NULL, mountpoint);
+ if (impl_share == NULL)
+ return (SA_NO_MEMORY);
+
fstype = fstypes;
while (fstype != NULL) {
- if (protocol == NULL || strcmp(fstype->name, protocol) == 0) {
- update_zfs_share(impl_share, fstype->name);
-
- rc = fstype->ops->enable_share(impl_share);
+ if (strcmp(fstype->name, protocol) == 0) {
+ rc = fstype->ops->disable_share(impl_share);
if (rc != SA_OK)
ret = rc;
- else
- FSINFO(impl_share, fstype)->active = B_TRUE;
found_protocol = B_TRUE;
}
fstype = fstype->next;
}
-
- update_sharetab(impl_share->handle);
+ free_share(impl_share);
return (found_protocol ? ret : SA_INVALID_PROTOCOL);
}
-int
-sa_disable_share(sa_share_t share, char *protocol)
+boolean_t
+sa_is_shared(const char *mountpoint, char *protocol)
{
- sa_share_impl_t impl_share = (sa_share_impl_t)share;
- int rc, ret = SA_OK;
- boolean_t found_protocol = B_FALSE;
sa_fstype_t *fstype;
+ boolean_t ret = B_FALSE;
+
+ /* guid value is not used */
+ sa_share_impl_t impl_share = alloc_share(NULL, mountpoint);
+ if (impl_share == NULL)
+ return (B_FALSE);
fstype = fstypes;
while (fstype != NULL) {
- if (protocol == NULL || strcmp(fstype->name, protocol) == 0) {
- rc = fstype->ops->disable_share(impl_share);
-
- if (rc == SA_OK) {
- fstype->ops->clear_shareopts(impl_share);
-
- FSINFO(impl_share, fstype)->active = B_FALSE;
- } else
- ret = rc;
-
- found_protocol = B_TRUE;
+ if (strcmp(fstype->name, protocol) == 0) {
+ ret = fstype->ops->is_shared(impl_share);
}
-
fstype = fstype->next;
}
+ free_share(impl_share);
+ return (ret);
+}
- update_sharetab(impl_share->handle);
-
- return (found_protocol ? ret : SA_INVALID_PROTOCOL);
+void
+sa_commit_shares(const char *protocol)
+{
+ sa_fstype_t *fstype = fstypes;
+ while (fstype != NULL) {
+ if (strcmp(fstype->name, protocol) == 0)
+ fstype->ops->commit_shares();
+ fstype = fstype->next;
+ }
}
/*
@@ -674,7 +296,7 @@ sa_errorstr(int err)
}
int
-sa_parse_legacy_options(sa_group_t group, char *options, char *proto)
+sa_validate_shareopts(char *options, char *proto)
{
sa_fstype_t *fstype;
@@ -691,25 +313,8 @@ sa_parse_legacy_options(sa_group_t group, char *options, char *proto)
return (SA_INVALID_PROTOCOL);
}
-boolean_t
-sa_needs_refresh(sa_handle_t handle)
-{
- return (B_TRUE);
-}
-
-libzfs_handle_t *
-sa_get_zfs_handle(sa_handle_t handle)
-{
- sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
-
- if (impl_handle == NULL)
- return (NULL);
-
- return (impl_handle->zfs_libhandle);
-}
-
static sa_share_impl_t
-alloc_share(const char *sharepath)
+alloc_share(const char *zfsname, const char *mountpoint)
{
sa_share_impl_t impl_share;
@@ -718,17 +323,24 @@ alloc_share(const char *sharepath)
if (impl_share == NULL)
return (NULL);
- impl_share->sharepath = strdup(sharepath);
-
- if (impl_share->sharepath == NULL) {
+ if (mountpoint != NULL &&
+ ((impl_share->sa_mountpoint = strdup(mountpoint)) == NULL)) {
free(impl_share);
return (NULL);
}
- impl_share->fsinfo = calloc(fstypes_count, sizeof (sa_share_fsinfo_t));
+ if (zfsname != NULL &&
+ ((impl_share->sa_zfsname = strdup(zfsname)) == NULL)) {
+ free(impl_share->sa_mountpoint);
+ free(impl_share);
+ return (NULL);
+ }
- if (impl_share->fsinfo == NULL) {
- free(impl_share->sharepath);
+ impl_share->sa_fsinfo = calloc(fstypes_count,
+ sizeof (sa_share_fsinfo_t));
+ if (impl_share->sa_fsinfo == NULL) {
+ free(impl_share->sa_mountpoint);
+ free(impl_share->sa_zfsname);
free(impl_share);
return (NULL);
}
@@ -744,26 +356,11 @@ free_share(sa_share_impl_t impl_share)
fstype = fstypes;
while (fstype != NULL) {
fstype->ops->clear_shareopts(impl_share);
-
- free(FSINFO(impl_share, fstype)->resource);
-
fstype = fstype->next;
}
- free(impl_share->sharepath);
- free(impl_share->dataset);
- free(impl_share->fsinfo);
+ free(impl_share->sa_mountpoint);
+ free(impl_share->sa_zfsname);
+ free(impl_share->sa_fsinfo);
free(impl_share);
}
-
-int
-sa_zfs_process_share(sa_handle_t handle, sa_group_t group, sa_share_t share,
- char *mountpoint, char *proto, zprop_source_t source, char *shareopts,
- char *sourcestr, char *dataset)
-{
- sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
- sa_share_impl_t impl_share = (sa_share_impl_t)share;
-
- return (process_share(impl_handle, impl_share, mountpoint, NULL,
- proto, shareopts, NULL, dataset, B_FALSE));
-}
diff --git a/lib/libshare/libshare_impl.h b/lib/libshare/libshare_impl.h
index 18d619b10..cc5ef4008 100644
--- a/lib/libshare/libshare_impl.h
+++ b/lib/libshare/libshare_impl.h
@@ -22,36 +22,32 @@
/*
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011 Gunnar Beutner
+ * Copyright (c) 2019, 2020 by Delphix. All rights reserved.
*/
-struct sa_handle_impl;
-
typedef struct sa_share_fsinfo {
- boolean_t active;
- char *resource;
char *shareopts;
} sa_share_fsinfo_t;
typedef struct sa_share_impl {
- struct sa_share_impl *next;
-
- struct sa_handle_impl *handle;
+ char *sa_mountpoint;
+ char *sa_zfsname;
- char *sharepath;
- char *dataset;
-
- sa_share_fsinfo_t *fsinfo; /* per-fstype information */
+ sa_share_fsinfo_t *sa_fsinfo; /* per-fstype information */
} *sa_share_impl_t;
-#define FSINFO(impl_share, fstype) (&(impl_share->fsinfo[fstype->fsinfo_index]))
+#define FSINFO(impl_share, fstype) \
+ (&(impl_share->sa_fsinfo[fstype->fsinfo_index]))
typedef struct sa_share_ops {
int (*enable_share)(sa_share_impl_t share);
int (*disable_share)(sa_share_impl_t share);
+ boolean_t (*is_shared)(sa_share_impl_t share);
int (*validate_shareopts)(const char *shareopts);
int (*update_shareopts)(sa_share_impl_t impl_share,
- const char *resource, const char *shareopts);
+ const char *shareopts);
void (*clear_shareopts)(sa_share_impl_t impl_share);
+ int (*commit_shares)(void);
} sa_share_ops_t;
typedef struct sa_fstype {
@@ -62,9 +58,4 @@ typedef struct sa_fstype {
int fsinfo_index;
} sa_fstype_t;
-typedef struct sa_handle_impl {
- libzfs_handle_t *zfs_libhandle;
- sa_share_impl_t shares;
-} *sa_handle_impl_t;
-
sa_fstype_t *register_fstype(const char *name, const sa_share_ops_t *ops);
diff --git a/lib/libshare/os/freebsd/nfs.c b/lib/libshare/os/freebsd/nfs.c
new file mode 100644
index 000000000..65f3b11bf
--- /dev/null
+++ b/lib/libshare/os/freebsd/nfs.c
@@ -0,0 +1,448 @@
+/*
+ * Copyright (c) 2007 Pawel Jakub Dawidek <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Copyright (c) 2020 by Delphix. All rights reserved.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/vfs.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <libintl.h>
+
+#include "libzfs_impl.h"
+#include "libshare_impl.h"
+#include "nfs.h"
+
+#define _PATH_MOUNTDPID "/var/run/mountd.pid"
+#define FILE_HEADER "# !!! DO NOT EDIT THIS FILE MANUALLY !!!\n\n"
+#define OPTSSIZE 1024
+#define MAXLINESIZE (PATH_MAX + OPTSSIZE)
+#define ZFS_EXPORTS_FILE "/etc/zfs/exports"
+#define ZFS_EXPORTS_LOCK ZFS_EXPORTS_FILE".lock"
+
+static sa_fstype_t *nfs_fstype;
+
+static int nfs_lock_fd = -1;
+
+/*
+ * The nfs_exports_[lock|unlock] is used to guard against conconcurrent
+ * updates to the exports file. Each protocol is responsible for
+ * providing the necessary locking to ensure consistency.
+ */
+static int
+nfs_exports_lock(void)
+{
+ nfs_lock_fd = open(ZFS_EXPORTS_LOCK,
+ O_RDWR | O_CREAT, 0600);
+ if (nfs_lock_fd == -1) {
+ fprintf(stderr, "failed to lock %s: %s\n",
+ ZFS_EXPORTS_LOCK, strerror(errno));
+ return (errno);
+ }
+ if (flock(nfs_lock_fd, LOCK_EX) != 0) {
+ fprintf(stderr, "failed to lock %s: %s\n",
+ ZFS_EXPORTS_LOCK, strerror(errno));
+ return (errno);
+ }
+ return (0);
+}
+
+static void
+nfs_exports_unlock(void)
+{
+ verify(nfs_lock_fd > 0);
+
+ if (flock(nfs_lock_fd, LOCK_UN) != 0) {
+ fprintf(stderr, "failed to unlock %s: %s\n",
+ ZFS_EXPORTS_LOCK, strerror(errno));
+ }
+ close(nfs_lock_fd);
+ nfs_lock_fd = -1;
+}
+
+/*
+ * Read one line from a file. Skip comments, empty lines and a line with a
+ * mountpoint specified in the 'skip' argument.
+ *
+ * NOTE: This function returns a static buffer and thus is not thread-safe.
+ */
+static char *
+zgetline(FILE *fd, const char *skip)
+{
+ static char line[MAXLINESIZE];
+ size_t len, skiplen = 0;
+ char *s, last;
+
+ if (skip != NULL)
+ skiplen = strlen(skip);
+ for (;;) {
+ s = fgets(line, sizeof (line), fd);
+ if (s == NULL)
+ return (NULL);
+ /* Skip empty lines and comments. */
+ if (line[0] == '\n' || line[0] == '#')
+ continue;
+ len = strlen(line);
+ if (line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ last = line[skiplen];
+ /* Skip the given mountpoint. */
+ if (skip != NULL && strncmp(skip, line, skiplen) == 0 &&
+ (last == '\t' || last == ' ' || last == '\0')) {
+ continue;
+ }
+ break;
+ }
+ return (line);
+}
+
+/*
+ * This function translate options to a format acceptable by exports(5), eg.
+ *
+ * -ro -network=192.168.0.0 -mask=255.255.255.0 -maproot=0 \
+ * zfs.freebsd.org 69.147.83.54
+ *
+ * Accepted input formats:
+ *
+ * ro,network=192.168.0.0,mask=255.255.255.0,maproot=0,zfs.freebsd.org
+ * ro network=192.168.0.0 mask=255.255.255.0 maproot=0 zfs.freebsd.org
+ * -ro,-network=192.168.0.0,-mask=255.255.255.0,-maproot=0,zfs.freebsd.org
+ * -ro -network=192.168.0.0 -mask=255.255.255.0 -maproot=0 \
+ * zfs.freebsd.org
+ *
+ * Recognized keywords:
+ *
+ * ro, maproot, mapall, mask, network, sec, alldirs, public, webnfs,
+ * index, quiet
+ *
+ * NOTE: This function returns a static buffer and thus is not thread-safe.
+ */
+static char *
+translate_opts(const char *shareopts)
+{
+ static const char *known_opts[] = { "ro", "maproot", "mapall", "mask",
+ "network", "sec", "alldirs", "public", "webnfs", "index", "quiet",
+ NULL };
+ static char newopts[OPTSSIZE];
+ char oldopts[OPTSSIZE];
+ char *o, *s = NULL;
+ unsigned int i;
+ size_t len;
+
+ strlcpy(oldopts, shareopts, sizeof (oldopts));
+ newopts[0] = '\0';
+ s = oldopts;
+ while ((o = strsep(&s, "-, ")) != NULL) {
+ if (o[0] == '\0')
+ continue;
+ for (i = 0; known_opts[i] != NULL; i++) {
+ len = strlen(known_opts[i]);
+ if (strncmp(known_opts[i], o, len) == 0 &&
+ (o[len] == '\0' || o[len] == '=')) {
+ strlcat(newopts, "-", sizeof (newopts));
+ break;
+ }
+ }
+ strlcat(newopts, o, sizeof (newopts));
+ strlcat(newopts, " ", sizeof (newopts));
+ }
+ return (newopts);
+}
+
+static char *
+nfs_init_tmpfile(void)
+{
+ char *tmpfile = NULL;
+
+ if (asprintf(&tmpfile, "%s%s", ZFS_EXPORTS_FILE, ".XXXXXXXX") == -1) {
+ fprintf(stderr, "Unable to allocate buffer for temporary "
+ "file name\n");
+ return (NULL);
+ }
+
+ int fd = mkstemp(tmpfile);
+ if (fd == -1) {
+ fprintf(stderr, "Unable to create temporary file: %s",
+ strerror(errno));
+ free(tmpfile);
+ return (NULL);
+ }
+ close(fd);
+ return (tmpfile);
+}
+
+static int
+nfs_fini_tmpfile(char *tmpfile)
+{
+ if (rename(tmpfile, ZFS_EXPORTS_FILE) == -1) {
+ fprintf(stderr, "Unable to rename %s: %s\n", tmpfile,
+ strerror(errno));
+ unlink(tmpfile);
+ free(tmpfile);
+ return (SA_SYSTEM_ERR);
+ }
+ free(tmpfile);
+ return (SA_OK);
+}
+
+/*
+ * This function copies all entries from the exports file to "filename",
+ * omitting any entries for the specified mountpoint.
+ */
+static int
+nfs_copy_entries(char *filename, const char *mountpoint)
+{
+ int error = SA_OK;
+ char *line;
+
+ /*
+ * If the file doesn't exist then there is nothing more
+ * we need to do.
+ */
+ FILE *oldfp = fopen(ZFS_EXPORTS_FILE, "r");
+ if (oldfp == NULL)
+ return (SA_OK);
+
+ FILE *newfp = fopen(filename, "w+");
+ fputs(FILE_HEADER, newfp);
+ while ((line = zgetline(oldfp, mountpoint)) != NULL)
+ fprintf(newfp, "%s\n", line);
+ if (ferror(oldfp) != 0) {
+ error = ferror(oldfp);
+ }
+ if (error == 0 && ferror(newfp) != 0) {
+ error = ferror(newfp);
+ }
+
+ if (fclose(newfp) != 0) {
+ fprintf(stderr, "Unable to close file %s: %s\n",
+ filename, strerror(errno));
+ error = error != 0 ? error : SA_SYSTEM_ERR;
+ }
+ fclose(oldfp);
+
+ return (error);
+}
+
+static int
+nfs_enable_share(sa_share_impl_t impl_share)
+{
+ char *filename = NULL;
+ int error;
+
+ if ((filename = nfs_init_tmpfile()) == NULL)
+ return (SA_SYSTEM_ERR);
+
+ error = nfs_exports_lock();
+ if (error != 0) {
+ unlink(filename);
+ free(filename);
+ return (error);
+ }
+
+ error = nfs_copy_entries(filename, impl_share->sa_mountpoint);
+ if (error != SA_OK) {
+ unlink(filename);
+ free(filename);
+ nfs_exports_unlock();
+ return (error);
+ }
+
+ FILE *fp = fopen(filename, "a+");
+ if (fp == NULL) {
+ fprintf(stderr, "failed to open %s file: %s", filename,
+ strerror(errno));
+ unlink(filename);
+ free(filename);
+ nfs_exports_unlock();
+ return (SA_SYSTEM_ERR);
+ }
+ char *shareopts = FSINFO(impl_share, nfs_fstype)->shareopts;
+ if (strcmp(shareopts, "on") == 0)
+ shareopts = "";
+
+ if (fprintf(fp, "%s\t%s\n", impl_share->sa_mountpoint,
+ translate_opts(shareopts)) < 0) {
+ fprintf(stderr, "failed to write to %s\n", filename);
+ fclose(fp);
+ unlink(filename);
+ free(filename);
+ nfs_exports_unlock();
+ return (SA_SYSTEM_ERR);
+ }
+
+ if (fclose(fp) != 0) {
+ fprintf(stderr, "Unable to close file %s: %s\n",
+ filename, strerror(errno));
+ unlink(filename);
+ free(filename);
+ nfs_exports_unlock();
+ return (SA_SYSTEM_ERR);
+ }
+ error = nfs_fini_tmpfile(filename);
+ nfs_exports_unlock();
+ return (error);
+}
+
+static int
+nfs_disable_share(sa_share_impl_t impl_share)
+{
+ int error;
+ char *filename = NULL;
+
+ if ((filename = nfs_init_tmpfile()) == NULL)
+ return (SA_SYSTEM_ERR);
+
+ error = nfs_exports_lock();
+ if (error != 0) {
+ unlink(filename);
+ free(filename);
+ return (error);
+ }
+
+ error = nfs_copy_entries(filename, impl_share->sa_mountpoint);
+ if (error != SA_OK) {
+ unlink(filename);
+ free(filename);
+ nfs_exports_unlock();
+ return (error);
+ }
+
+ error = nfs_fini_tmpfile(filename);
+ nfs_exports_unlock();
+ return (error);
+}
+
+/*
+ * NOTE: This function returns a static buffer and thus is not thread-safe.
+ */
+static boolean_t
+nfs_is_shared(sa_share_impl_t impl_share)
+{
+ static char line[MAXLINESIZE];
+ char *s, last;
+ size_t len;
+ char *mntpoint = impl_share->sa_mountpoint;
+ size_t mntlen = strlen(mntpoint);
+
+ FILE *fp = fopen(ZFS_EXPORTS_FILE, "r");
+ if (fp == NULL)
+ return (B_FALSE);
+
+ for (;;) {
+ s = fgets(line, sizeof (line), fp);
+ if (s == NULL)
+ return (B_FALSE);
+ /* Skip empty lines and comments. */
+ if (line[0] == '\n' || line[0] == '#')
+ continue;
+ len = strlen(line);
+ if (line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ last = line[mntlen];
+ /* Skip the given mountpoint. */
+ if (strncmp(mntpoint, line, mntlen) == 0 &&
+ (last == '\t' || last == ' ' || last == '\0')) {
+ fclose(fp);
+ return (B_TRUE);
+ }
+ }
+ fclose(fp);
+ return (B_FALSE);
+}
+
+static int
+nfs_validate_shareopts(const char *shareopts)
+{
+ return (SA_OK);
+}
+
+static int
+nfs_update_shareopts(sa_share_impl_t impl_share, const char *shareopts)
+{
+ FSINFO(impl_share, nfs_fstype)->shareopts = (char *)shareopts;
+ return (SA_OK);
+}
+
+static void
+nfs_clear_shareopts(sa_share_impl_t impl_share)
+{
+ FSINFO(impl_share, nfs_fstype)->shareopts = NULL;
+}
+
+/*
+ * Commit the shares by restarting mountd.
+ */
+static int
+nfs_commit_shares(void)
+{
+ struct pidfh *pfh;
+ pid_t mountdpid;
+
+ pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &mountdpid);
+ if (pfh != NULL) {
+ /* Mountd is not running. */
+ pidfile_remove(pfh);
+ return (SA_OK);
+ }
+ if (errno != EEXIST) {
+ /* Cannot open pidfile for some reason. */
+ return (SA_SYSTEM_ERR);
+ }
+ /* We have mountd(8) PID in mountdpid variable. */
+ kill(mountdpid, SIGHUP);
+ return (SA_OK);
+}
+
+static const sa_share_ops_t nfs_shareops = {
+ .enable_share = nfs_enable_share,
+ .disable_share = nfs_disable_share,
+ .is_shared = nfs_is_shared,
+
+ .validate_shareopts = nfs_validate_shareopts,
+ .update_shareopts = nfs_update_shareopts,
+ .clear_shareopts = nfs_clear_shareopts,
+ .commit_shares = nfs_commit_shares,
+};
+
+/*
+ * Initializes the NFS functionality of libshare.
+ */
+void
+libshare_nfs_init(void)
+{
+ nfs_fstype = register_fstype("nfs", &nfs_shareops);
+}
diff --git a/lib/libshare/os/freebsd/smb.c b/lib/libshare/os/freebsd/smb.c
new file mode 100644
index 000000000..5b606ab96
--- /dev/null
+++ b/lib/libshare/os/freebsd/smb.c
@@ -0,0 +1,128 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2020 by Delphix. All rights reserved.
+ */
+
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <libzfs.h>
+#include <libshare.h>
+#include "libshare_impl.h"
+#include "smb.h"
+
+static sa_fstype_t *smb_fstype;
+
+/*
+ * Enables SMB sharing for the specified share.
+ */
+static int
+smb_enable_share(sa_share_impl_t impl_share)
+{
+ fprintf(stderr, "No SMB support in FreeBSD yet.\n");
+ return (SA_NOT_SUPPORTED);
+}
+/*
+ * Disables SMB sharing for the specified share.
+ */
+static int
+smb_disable_share(sa_share_impl_t impl_share)
+{
+ fprintf(stderr, "No SMB support in FreeBSD yet.\n");
+ return (SA_NOT_SUPPORTED);
+}
+
+/*
+ * Checks whether the specified SMB share options are syntactically correct.
+ */
+static int
+smb_validate_shareopts(const char *shareopts)
+{
+ fprintf(stderr, "No SMB support in FreeBSD yet.\n");
+ return (SA_NOT_SUPPORTED);
+}
+
+/*
+ * Checks whether a share is currently active.
+ */
+static boolean_t
+smb_is_share_active(sa_share_impl_t impl_share)
+{
+ return (B_FALSE);
+}
+
+/*
+ * Called to update a share's options. A share's options might be out of
+ * date if the share was loaded from disk and the "sharesmb" dataset
+ * property has changed in the meantime. This function also takes care
+ * of re-enabling the share if necessary.
+ */
+static int
+smb_update_shareopts(sa_share_impl_t impl_share, const char *shareopts)
+{
+ return (SA_OK);
+}
+
+static int
+smb_update_shares(void)
+{
+ /* Not implemented */
+ return (0);
+}
+/*
+ * Clears a share's SMB options. Used by libshare to
+ * clean up shares that are about to be free()'d.
+ */
+static void
+smb_clear_shareopts(sa_share_impl_t impl_share)
+{
+ FSINFO(impl_share, smb_fstype)->shareopts = NULL;
+}
+
+static const sa_share_ops_t smb_shareops = {
+ .enable_share = smb_enable_share,
+ .disable_share = smb_disable_share,
+ .is_shared = smb_is_share_active,
+
+ .validate_shareopts = smb_validate_shareopts,
+ .update_shareopts = smb_update_shareopts,
+ .clear_shareopts = smb_clear_shareopts,
+ .commit_shares = smb_update_shares,
+};
+
+/*
+ * Initializes the SMB functionality of libshare.
+ */
+void
+libshare_smb_init(void)
+{
+ smb_fstype = register_fstype("smb", &smb_shareops);
+}
diff --git a/lib/libshare/nfs.c b/lib/libshare/os/linux/nfs.c
index c45f25cd0..bc949213f 100644
--- a/lib/libshare/nfs.c
+++ b/lib/libshare/os/linux/nfs.c
@@ -23,13 +23,17 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011 Gunnar Beutner
* Copyright (c) 2012 Cyril Plisko. All rights reserved.
+ * Copyright (c) 2019, 2020 by Delphix. All rights reserved.
*/
+#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
-#include <fcntl.h>
#include <errno.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <libzfs.h>
@@ -37,21 +41,56 @@
#include "libshare_impl.h"
#include "nfs.h"
-static boolean_t nfs_available(void);
+#define FILE_HEADER "# !!! DO NOT EDIT THIS FILE MANUALLY !!!\n\n"
+#define ZFS_EXPORTS_DIR "/etc/exports.d"
+#define ZFS_EXPORTS_FILE ZFS_EXPORTS_DIR"/zfs.exports"
+#define ZFS_EXPORTS_LOCK ZFS_EXPORTS_FILE".lock"
static sa_fstype_t *nfs_fstype;
+typedef int (*nfs_shareopt_callback_t)(const char *opt, const char *value,
+ void *cookie);
+
+typedef int (*nfs_host_callback_t)(const char *sharepath, const char *filename,
+ const char *host, const char *security, const char *access, void *cookie);
+
+static int nfs_lock_fd = -1;
+
/*
- * nfs_exportfs_temp_fd refers to a temporary copy of the output
- * from exportfs -v.
+ * The nfs_exports_[lock|unlock] is used to guard against conconcurrent
+ * updates to the exports file. Each protocol is responsible for
+ * providing the necessary locking to ensure consistency.
*/
-static int nfs_exportfs_temp_fd = -1;
+static int
+nfs_exports_lock(void)
+{
+ nfs_lock_fd = open(ZFS_EXPORTS_LOCK,
+ O_RDWR | O_CREAT, 0600);
+ if (nfs_lock_fd == -1) {
+ fprintf(stderr, "failed to lock %s: %s\n",
+ ZFS_EXPORTS_LOCK, strerror(errno));
+ return (errno);
+ }
+ if (flock(nfs_lock_fd, LOCK_EX) != 0) {
+ fprintf(stderr, "failed to lock %s: %s\n",
+ ZFS_EXPORTS_LOCK, strerror(errno));
+ return (errno);
+ }
+ return (0);
+}
-typedef int (*nfs_shareopt_callback_t)(const char *opt, const char *value,
- void *cookie);
+static void
+nfs_exports_unlock(void)
+{
+ verify(nfs_lock_fd > 0);
-typedef int (*nfs_host_callback_t)(const char *sharepath, const char *host,
- const char *security, const char *access, void *cookie);
+ if (flock(nfs_lock_fd, LOCK_UN) != 0) {
+ fprintf(stderr, "failed to unlock %s: %s\n",
+ ZFS_EXPORTS_LOCK, strerror(errno));
+ }
+ close(nfs_lock_fd);
+ nfs_lock_fd = -1;
+}
/*
* Invokes the specified callback function for each Solaris share option
@@ -62,13 +101,17 @@ foreach_nfs_shareopt(const char *shareopts,
nfs_shareopt_callback_t callback, void *cookie)
{
char *shareopts_dup, *opt, *cur, *value;
- int was_nul, rc;
+ int was_nul, error;
if (shareopts == NULL)
return (SA_OK);
+ if (strcmp(shareopts, "on") == 0)
+ shareopts = "rw,crossmnt";
+
shareopts_dup = strdup(shareopts);
+
if (shareopts_dup == NULL)
return (SA_NO_MEMORY);
@@ -94,11 +137,11 @@ foreach_nfs_shareopt(const char *shareopts,
value++;
}
- rc = callback(opt, value, cookie);
+ error = callback(opt, value, cookie);
- if (rc != SA_OK) {
+ if (error != SA_OK) {
free(shareopts_dup);
- return (rc);
+ return (error);
}
}
@@ -110,13 +153,14 @@ foreach_nfs_shareopt(const char *shareopts,
free(shareopts_dup);
- return (0);
+ return (SA_OK);
}
typedef struct nfs_host_cookie_s {
nfs_host_callback_t callback;
const char *sharepath;
void *cookie;
+ const char *filename;
const char *security;
} nfs_host_cookie_t;
@@ -128,7 +172,7 @@ typedef struct nfs_host_cookie_s {
static int
foreach_nfs_host_cb(const char *opt, const char *value, void *pcookie)
{
- int rc;
+ int error;
const char *access;
char *host_dup, *host, *next;
nfs_host_cookie_t *udata = (nfs_host_cookie_t *)pcookie;
@@ -160,13 +204,14 @@ foreach_nfs_host_cb(const char *opt, const char *value, void *pcookie)
next++;
}
- rc = udata->callback(udata->sharepath, host,
- udata->security, access, udata->cookie);
+ error = udata->callback(udata->filename,
+ udata->sharepath, host, udata->security,
+ access, udata->cookie);
- if (rc != SA_OK) {
+ if (error != SA_OK) {
free(host_dup);
- return (rc);
+ return (error);
}
host = next;
@@ -182,21 +227,22 @@ foreach_nfs_host_cb(const char *opt, const char *value, void *pcookie)
* Invokes a callback function for all NFS hosts that are set for a share.
*/
static int
-foreach_nfs_host(sa_share_impl_t impl_share, nfs_host_callback_t callback,
- void *cookie)
+foreach_nfs_host(sa_share_impl_t impl_share, char *filename,
+ nfs_host_callback_t callback, void *cookie)
{
nfs_host_cookie_t udata;
char *shareopts;
udata.callback = callback;
- udata.sharepath = impl_share->sharepath;
+ udata.sharepath = impl_share->sa_mountpoint;
udata.cookie = cookie;
+ udata.filename = filename;
udata.security = "sys";
shareopts = FSINFO(impl_share, nfs_fstype)->shareopts;
- return foreach_nfs_shareopt(shareopts, foreach_nfs_host_cb,
- &udata);
+ return (foreach_nfs_shareopt(shareopts, foreach_nfs_host_cb,
+ &udata));
}
/*
@@ -227,69 +273,6 @@ get_linux_hostspec(const char *solaris_hostspec, char **plinux_hostspec)
}
/*
- * Used internally by nfs_enable_share to enable sharing for a single host.
- */
-static int
-nfs_enable_share_one(const char *sharepath, const char *host,
- const char *security, const char *access, void *pcookie)
-{
- int rc;
- char *linuxhost, *hostpath, *opts;
- const char *linux_opts = (const char *)pcookie;
- char *argv[6];
-
- /* exportfs -i -o sec=XX,rX,<opts> <host>:<sharepath> */
-
- rc = get_linux_hostspec(host, &linuxhost);
-
- if (rc < 0)
- exit(1);
-
- hostpath = malloc(strlen(linuxhost) + 1 + strlen(sharepath) + 1);
-
- if (hostpath == NULL) {
- free(linuxhost);
-
- exit(1);
- }
-
- sprintf(hostpath, "%s:%s", linuxhost, sharepath);
-
- free(linuxhost);
-
- if (linux_opts == NULL)
- linux_opts = "";
-
- opts = malloc(4 + strlen(security) + 4 + strlen(linux_opts) + 1);
-
- if (opts == NULL)
- exit(1);
-
- sprintf(opts, "sec=%s,%s,%s", security, access, linux_opts);
-
-#ifdef DEBUG
- fprintf(stderr, "sharing %s with opts %s\n", hostpath, opts);
-#endif
-
- argv[0] = "/usr/sbin/exportfs";
- argv[1] = "-i";
- argv[2] = "-o";
- argv[3] = opts;
- argv[4] = hostpath;
- argv[5] = NULL;
-
- rc = libzfs_run_process(argv[0], argv, 0);
-
- free(hostpath);
- free(opts);
-
- if (rc < 0)
- return (SA_SYSTEM_ERR);
- else
- return (SA_OK);
-}
-
-/*
* Adds a Linux share option to an array of NFS options.
*/
static int
@@ -382,7 +365,7 @@ get_linux_shareopts_cb(const char *key, const char *value, void *cookie)
static int
get_linux_shareopts(const char *shareopts, char **plinux_opts)
{
- int rc;
+ int error;
assert(plinux_opts != NULL);
@@ -394,237 +377,287 @@ get_linux_shareopts(const char *shareopts, char **plinux_opts)
/* mountpoint - Restrict exports to ZFS mountpoints */
(void) add_linux_shareopt(plinux_opts, "mountpoint", NULL);
- rc = foreach_nfs_shareopt(shareopts, get_linux_shareopts_cb,
+ error = foreach_nfs_shareopt(shareopts, get_linux_shareopts_cb,
plinux_opts);
- if (rc != SA_OK) {
+ if (error != SA_OK) {
free(*plinux_opts);
*plinux_opts = NULL;
}
- return (rc);
+ return (error);
}
-/*
- * Enables NFS sharing for the specified share.
- */
-static int
-nfs_enable_share(sa_share_impl_t impl_share)
+static char *
+nfs_init_tmpfile(void)
{
- char *shareopts, *linux_opts;
- int rc;
+ char *tmpfile = NULL;
- if (!nfs_available()) {
- return (SA_SYSTEM_ERR);
+ if (asprintf(&tmpfile, "%s%s", ZFS_EXPORTS_FILE, ".XXXXXXXX") == -1) {
+ fprintf(stderr, "Unable to allocate temporary file\n");
+ return (NULL);
}
- shareopts = FSINFO(impl_share, nfs_fstype)->shareopts;
-
- if (shareopts == NULL)
- return (SA_OK);
-
- rc = get_linux_shareopts(shareopts, &linux_opts);
-
- if (rc != SA_OK)
- return (rc);
-
- rc = foreach_nfs_host(impl_share, nfs_enable_share_one, linux_opts);
-
- free(linux_opts);
+ int fd = mkstemp(tmpfile);
+ if (fd == -1) {
+ fprintf(stderr, "Unable to create temporary file: %s",
+ strerror(errno));
+ free(tmpfile);
+ return (NULL);
+ }
+ close(fd);
+ return (tmpfile);
+}
- return (rc);
+static int
+nfs_fini_tmpfile(char *tmpfile)
+{
+ if (rename(tmpfile, ZFS_EXPORTS_FILE) == -1) {
+ fprintf(stderr, "Unable to rename %s: %s\n", tmpfile,
+ strerror(errno));
+ unlink(tmpfile);
+ free(tmpfile);
+ return (SA_SYSTEM_ERR);
+ }
+ free(tmpfile);
+ return (SA_OK);
}
/*
- * Used internally by nfs_disable_share to disable sharing for a single host.
+ * This function populates an entry into /etc/exports.d/zfs.exports.
+ * This file is consumed by the linux nfs server so that zfs shares are
+ * automatically exported upon boot or whenever the nfs server restarts.
*/
static int
-nfs_disable_share_one(const char *sharepath, const char *host,
- const char *security, const char *access, void *cookie)
+nfs_add_entry(const char *filename, const char *sharepath,
+ const char *host, const char *security, const char *access_opts,
+ void *pcookie)
{
- int rc;
- char *linuxhost, *hostpath;
- char *argv[4];
-
- rc = get_linux_hostspec(host, &linuxhost);
+ int error;
+ char *linuxhost;
+ const char *linux_opts = (const char *)pcookie;
- if (rc < 0)
- exit(1);
+ error = get_linux_hostspec(host, &linuxhost);
+ if (error != SA_OK)
+ return (error);
- hostpath = malloc(strlen(linuxhost) + 1 + strlen(sharepath) + 1);
+ if (linux_opts == NULL)
+ linux_opts = "";
- if (hostpath == NULL) {
+ FILE *fp = fopen(filename, "a+");
+ if (fp == NULL) {
+ fprintf(stderr, "failed to open %s file: %s", filename,
+ strerror(errno));
free(linuxhost);
- exit(1);
+ return (SA_SYSTEM_ERR);
}
- sprintf(hostpath, "%s:%s", linuxhost, sharepath);
+ if (fprintf(fp, "%s %s(sec=%s,%s,%s)\n", sharepath, linuxhost,
+ security, access_opts, linux_opts) < 0) {
+ fprintf(stderr, "failed to write to %s\n", filename);
+ free(linuxhost);
+ fclose(fp);
+ return (SA_SYSTEM_ERR);
+ }
free(linuxhost);
-
-#ifdef DEBUG
- fprintf(stderr, "unsharing %s\n", hostpath);
-#endif
-
- argv[0] = "/usr/sbin/exportfs";
- argv[1] = "-u";
- argv[2] = hostpath;
- argv[3] = NULL;
-
- rc = libzfs_run_process(argv[0], argv, 0);
-
- free(hostpath);
-
- if (rc < 0)
+ if (fclose(fp) != 0) {
+ fprintf(stderr, "Unable to close file %s: %s\n",
+ filename, strerror(errno));
return (SA_SYSTEM_ERR);
- else
- return (SA_OK);
+ }
+ return (SA_OK);
}
/*
- * Disables NFS sharing for the specified share.
+ * This function copies all entries from the exports file to "filename",
+ * omitting any entries for the specified mountpoint.
*/
static int
-nfs_disable_share(sa_share_impl_t impl_share)
+nfs_copy_entries(char *filename, const char *mountpoint)
{
- if (!nfs_available()) {
- /*
- * The share can't possibly be active, so nothing
- * needs to be done to disable it.
- */
+ char *buf = NULL;
+ size_t buflen = 0;
+ int error = SA_OK;
+
+ /*
+ * If the file doesn't exist then there is nothing more
+ * we need to do.
+ */
+ FILE *oldfp = fopen(ZFS_EXPORTS_FILE, "r");
+ if (oldfp == NULL)
return (SA_OK);
+
+ FILE *newfp = fopen(filename, "w+");
+ fputs(FILE_HEADER, newfp);
+ while ((getline(&buf, &buflen, oldfp)) != -1) {
+ char *space = NULL;
+
+ if (buf[0] == '\n' || buf[0] == '#')
+ continue;
+
+ if ((space = strchr(buf, ' ')) != NULL) {
+ int mountpoint_len = strlen(mountpoint);
+
+ if (space - buf == mountpoint_len &&
+ strncmp(mountpoint, buf, mountpoint_len) == 0) {
+ continue;
+ }
+ }
+ fputs(buf, newfp);
+ }
+
+ if (oldfp != NULL && ferror(oldfp) != 0) {
+ error = ferror(oldfp);
+ }
+ if (error == 0 && ferror(newfp) != 0) {
+ error = ferror(newfp);
}
- return (foreach_nfs_host(impl_share, nfs_disable_share_one, NULL));
+ free(buf);
+ if (fclose(newfp) != 0) {
+ fprintf(stderr, "Unable to close file %s: %s\n",
+ filename, strerror(errno));
+ error = error != 0 ? error : SA_SYSTEM_ERR;
+ }
+ fclose(oldfp);
+
+ return (error);
}
/*
- * Checks whether the specified NFS share options are syntactically correct.
+ * Enables NFS sharing for the specified share.
*/
static int
-nfs_validate_shareopts(const char *shareopts)
+nfs_enable_share(sa_share_impl_t impl_share)
{
- char *linux_opts;
- int rc;
+ char *shareopts, *linux_opts;
+ char *filename = NULL;
+ int error;
- rc = get_linux_shareopts(shareopts, &linux_opts);
+ if ((filename = nfs_init_tmpfile()) == NULL)
+ return (SA_SYSTEM_ERR);
- if (rc != SA_OK)
- return (rc);
+ error = nfs_exports_lock();
+ if (error != 0) {
+ unlink(filename);
+ free(filename);
+ return (error);
+ }
- free(linux_opts);
+ error = nfs_copy_entries(filename, impl_share->sa_mountpoint);
+ if (error != SA_OK) {
+ unlink(filename);
+ free(filename);
+ nfs_exports_unlock();
+ return (error);
+ }
- return (SA_OK);
+ shareopts = FSINFO(impl_share, nfs_fstype)->shareopts;
+ error = get_linux_shareopts(shareopts, &linux_opts);
+ if (error != SA_OK) {
+ unlink(filename);
+ free(filename);
+ nfs_exports_unlock();
+ return (error);
+ }
+
+ error = foreach_nfs_host(impl_share, filename, nfs_add_entry,
+ linux_opts);
+ free(linux_opts);
+ if (error == 0) {
+ error = nfs_fini_tmpfile(filename);
+ } else {
+ unlink(filename);
+ free(filename);
+ }
+ nfs_exports_unlock();
+ return (error);
}
/*
- * Checks whether a share is currently active.
+ * Disables NFS sharing for the specified share.
*/
-static boolean_t
-nfs_is_share_active(sa_share_impl_t impl_share)
+static int
+nfs_disable_share(sa_share_impl_t impl_share)
{
- int fd;
- char line[512];
- char *tab, *cur;
- FILE *nfs_exportfs_temp_fp;
+ int error;
+ char *filename = NULL;
- if (!nfs_available())
- return (B_FALSE);
+ if ((filename = nfs_init_tmpfile()) == NULL)
+ return (SA_SYSTEM_ERR);
- if ((fd = dup(nfs_exportfs_temp_fd)) == -1)
- return (B_FALSE);
+ error = nfs_exports_lock();
+ if (error != 0) {
+ unlink(filename);
+ free(filename);
+ return (error);
+ }
- nfs_exportfs_temp_fp = fdopen(fd, "r");
+ error = nfs_copy_entries(filename, impl_share->sa_mountpoint);
+ if (error != SA_OK) {
+ unlink(filename);
+ free(filename);
+ nfs_exports_unlock();
+ return (error);
+ }
+ error = nfs_fini_tmpfile(filename);
+ nfs_exports_unlock();
+ return (error);
+}
- if (nfs_exportfs_temp_fp == NULL)
- return (B_FALSE);
+static boolean_t
+nfs_is_shared(sa_share_impl_t impl_share)
+{
+ size_t buflen = 0;
+ char *buf = NULL;
- if (fseek(nfs_exportfs_temp_fp, 0, SEEK_SET) < 0) {
- fclose(nfs_exportfs_temp_fp);
+ FILE *fp = fopen(ZFS_EXPORTS_FILE, "r");
+ if (fp == NULL) {
return (B_FALSE);
}
-
- while (fgets(line, sizeof (line), nfs_exportfs_temp_fp) != NULL) {
- /*
- * exportfs uses separate lines for the share path
- * and the export options when the share path is longer
- * than a certain amount of characters; this ignores
- * the option lines
- */
- if (line[0] == '\t')
- continue;
-
- tab = strchr(line, '\t');
-
- if (tab != NULL) {
- *tab = '\0';
- cur = tab - 1;
- } else {
- /*
- * there's no tab character, which means the
- * NFS options are on a separate line; we just
- * need to remove the new-line character
- * at the end of the line
- */
- cur = line + strlen(line) - 1;
- }
-
- /* remove trailing spaces and new-line characters */
- while (cur >= line && (*cur == ' ' || *cur == '\n'))
- *cur-- = '\0';
-
- if (strcmp(line, impl_share->sharepath) == 0) {
- fclose(nfs_exportfs_temp_fp);
- return (B_TRUE);
+ while ((getline(&buf, &buflen, fp)) != -1) {
+ char *space = NULL;
+
+ if ((space = strchr(buf, ' ')) != NULL) {
+ int mountpoint_len = strlen(impl_share->sa_mountpoint);
+
+ if (space - buf == mountpoint_len &&
+ strncmp(impl_share->sa_mountpoint, buf,
+ mountpoint_len) == 0) {
+ fclose(fp);
+ free(buf);
+ return (B_TRUE);
+ }
}
}
-
- fclose(nfs_exportfs_temp_fp);
-
+ free(buf);
+ fclose(fp);
return (B_FALSE);
}
/*
- * Called to update a share's options. A share's options might be out of
- * date if the share was loaded from disk (i.e. /etc/dfs/sharetab) and the
- * "sharenfs" dataset property has changed in the meantime. This function
- * also takes care of re-enabling the share if necessary.
+ * Checks whether the specified NFS share options are syntactically correct.
*/
static int
-nfs_update_shareopts(sa_share_impl_t impl_share, const char *resource,
- const char *shareopts)
+nfs_validate_shareopts(const char *shareopts)
{
- char *shareopts_dup;
- boolean_t needs_reshare = B_FALSE;
- char *old_shareopts;
-
- FSINFO(impl_share, nfs_fstype)->active =
- nfs_is_share_active(impl_share);
-
- old_shareopts = FSINFO(impl_share, nfs_fstype)->shareopts;
-
- if (strcmp(shareopts, "on") == 0)
- shareopts = "rw,crossmnt";
-
- if (FSINFO(impl_share, nfs_fstype)->active && old_shareopts != NULL &&
- strcmp(old_shareopts, shareopts) != 0) {
- needs_reshare = B_TRUE;
- nfs_disable_share(impl_share);
- }
-
- shareopts_dup = strdup(shareopts);
-
- if (shareopts_dup == NULL)
- return (SA_NO_MEMORY);
+ char *linux_opts;
+ int error;
- if (old_shareopts != NULL)
- free(old_shareopts);
+ error = get_linux_shareopts(shareopts, &linux_opts);
- FSINFO(impl_share, nfs_fstype)->shareopts = shareopts_dup;
+ if (error != SA_OK)
+ return (error);
- if (needs_reshare)
- nfs_enable_share(impl_share);
+ free(linux_opts);
+ return (SA_OK);
+}
+static int
+nfs_update_shareopts(sa_share_impl_t impl_share, const char *shareopts)
+{
+ FSINFO(impl_share, nfs_fstype)->shareopts = (char *)shareopts;
return (SA_OK);
}
@@ -635,114 +668,45 @@ nfs_update_shareopts(sa_share_impl_t impl_share, const char *resource,
static void
nfs_clear_shareopts(sa_share_impl_t impl_share)
{
- free(FSINFO(impl_share, nfs_fstype)->shareopts);
FSINFO(impl_share, nfs_fstype)->shareopts = NULL;
}
+static int
+nfs_commit_shares(void)
+{
+ char *argv[] = {
+ "/usr/sbin/exportfs",
+ "-ra",
+ NULL
+ };
+
+ return (libzfs_run_process(argv[0], argv, 0));
+}
+
static const sa_share_ops_t nfs_shareops = {
.enable_share = nfs_enable_share,
.disable_share = nfs_disable_share,
+ .is_shared = nfs_is_shared,
.validate_shareopts = nfs_validate_shareopts,
.update_shareopts = nfs_update_shareopts,
.clear_shareopts = nfs_clear_shareopts,
+ .commit_shares = nfs_commit_shares,
};
/*
- * nfs_check_exportfs() checks that the exportfs command runs
- * and also maintains a temporary copy of the output from
- * exportfs -v.
- * To update this temporary copy simply call this function again.
- *
- * TODO : Use /var/lib/nfs/etab instead of our private copy.
- * But must implement locking to prevent concurrent access.
- *
- * TODO : The temporary file descriptor is never closed since
- * there is no libshare_nfs_fini() function.
- */
-static int
-nfs_check_exportfs(void)
-{
- pid_t pid;
- int rc, status;
- static char nfs_exportfs_tempfile[] = "/tmp/exportfs.XXXXXX";
-
- /*
- * Close any existing temporary copies of output from exportfs.
- * We have already called unlink() so file will be deleted.
- */
- if (nfs_exportfs_temp_fd >= 0)
- close(nfs_exportfs_temp_fd);
-
- nfs_exportfs_temp_fd = mkstemp(nfs_exportfs_tempfile);
-
- if (nfs_exportfs_temp_fd < 0)
- return (SA_SYSTEM_ERR);
-
- unlink(nfs_exportfs_tempfile);
-
- (void) fcntl(nfs_exportfs_temp_fd, F_SETFD, FD_CLOEXEC);
-
- pid = fork();
-
- if (pid < 0) {
- (void) close(nfs_exportfs_temp_fd);
- nfs_exportfs_temp_fd = -1;
- return (SA_SYSTEM_ERR);
- }
-
- if (pid > 0) {
- while ((rc = waitpid(pid, &status, 0)) <= 0 &&
- errno == EINTR) { }
-
- if (rc <= 0) {
- (void) close(nfs_exportfs_temp_fd);
- nfs_exportfs_temp_fd = -1;
- return (SA_SYSTEM_ERR);
- }
-
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- (void) close(nfs_exportfs_temp_fd);
- nfs_exportfs_temp_fd = -1;
- return (SA_CONFIG_ERR);
- }
-
- return (SA_OK);
- }
-
- /* child */
-
- /* exportfs -v */
-
- if (dup2(nfs_exportfs_temp_fd, STDOUT_FILENO) < 0)
- exit(1);
-
- rc = execlp("/usr/sbin/exportfs", "exportfs", "-v", NULL);
-
- if (rc < 0) {
- exit(1);
- }
-
- exit(0);
-}
-
-/*
- * Provides a convenient wrapper for determining nfs availability
- */
-static boolean_t
-nfs_available(void)
-{
- if (nfs_exportfs_temp_fd == -1)
- (void) nfs_check_exportfs();
-
- return ((nfs_exportfs_temp_fd != -1) ? B_TRUE : B_FALSE);
-}
-
-/*
* Initializes the NFS functionality of libshare.
*/
void
libshare_nfs_init(void)
{
+ struct stat sb;
+
nfs_fstype = register_fstype("nfs", &nfs_shareops);
+
+ if (stat(ZFS_EXPORTS_DIR, &sb) < 0 &&
+ mkdir(ZFS_EXPORTS_DIR, 0755) < 0) {
+ fprintf(stderr, "failed to create %s: %s\n",
+ ZFS_EXPORTS_DIR, strerror(errno));
+ }
}
diff --git a/lib/libshare/smb.c b/lib/libshare/os/linux/smb.c
index f567f7c49..3dcf666eb 100644
--- a/lib/libshare/smb.c
+++ b/lib/libshare/os/linux/smb.c
@@ -23,6 +23,7 @@
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011,2012 Turbo Fredriksson <[email protected]>, based on nfs.c
* by Gunnar Beutner
+ * Copyright (c) 2019, 2020 by Delphix. All rights reserved.
*
* This is an addition to the zfs device driver to add, modify and remove SMB
* shares using the 'net share' command that comes with Samba.
@@ -66,6 +67,8 @@ static boolean_t smb_available(void);
static sa_fstype_t *smb_fstype;
smb_share_t *smb_shares;
+static int smb_disable_share(sa_share_impl_t impl_share);
+static boolean_t smb_is_share_active(sa_share_impl_t impl_share);
/*
* Retrieve the list of SMB shares.
@@ -275,6 +278,9 @@ smb_enable_share(sa_share_impl_t impl_share)
if (!smb_available())
return (SA_SYSTEM_ERR);
+ if (smb_is_share_active(impl_share))
+ smb_disable_share(impl_share);
+
shareopts = FSINFO(impl_share, smb_fstype)->shareopts;
if (shareopts == NULL) /* on/off */
return (SA_SYSTEM_ERR);
@@ -283,8 +289,8 @@ smb_enable_share(sa_share_impl_t impl_share)
return (SA_OK);
/* Magic: Enable (i.e., 'create new') share */
- return (smb_enable_share_one(impl_share->dataset,
- impl_share->sharepath));
+ return (smb_enable_share_one(impl_share->sa_zfsname,
+ impl_share->sa_mountpoint));
}
/*
@@ -329,7 +335,7 @@ smb_disable_share(sa_share_impl_t impl_share)
}
while (shares != NULL) {
- if (strcmp(impl_share->sharepath, shares->path) == 0)
+ if (strcmp(impl_share->sa_mountpoint, shares->path) == 0)
return (smb_disable_share_one(shares->name));
shares = shares->next;
@@ -366,7 +372,7 @@ smb_is_share_active(sa_share_impl_t impl_share)
smb_retrieve_shares();
while (iter != NULL) {
- if (strcmp(impl_share->sharepath, iter->path) == 0)
+ if (strcmp(impl_share->sa_mountpoint, iter->path) == 0)
return (B_TRUE);
iter = iter->next;
@@ -382,43 +388,22 @@ smb_is_share_active(sa_share_impl_t impl_share)
* of re-enabling the share if necessary.
*/
static int
-smb_update_shareopts(sa_share_impl_t impl_share, const char *resource,
- const char *shareopts)
+smb_update_shareopts(sa_share_impl_t impl_share, const char *shareopts)
{
- char *shareopts_dup;
- boolean_t needs_reshare = B_FALSE;
- char *old_shareopts;
-
if (!impl_share)
return (SA_SYSTEM_ERR);
- FSINFO(impl_share, smb_fstype)->active =
- smb_is_share_active(impl_share);
-
- old_shareopts = FSINFO(impl_share, smb_fstype)->shareopts;
-
- if (FSINFO(impl_share, smb_fstype)->active && old_shareopts != NULL &&
- strcmp(old_shareopts, shareopts) != 0) {
- needs_reshare = B_TRUE;
- smb_disable_share(impl_share);
- }
-
- shareopts_dup = strdup(shareopts);
-
- if (shareopts_dup == NULL)
- return (SA_NO_MEMORY);
-
- if (old_shareopts != NULL)
- free(old_shareopts);
-
- FSINFO(impl_share, smb_fstype)->shareopts = shareopts_dup;
-
- if (needs_reshare)
- smb_enable_share(impl_share);
-
+ FSINFO(impl_share, smb_fstype)->shareopts = (char *)shareopts;
return (SA_OK);
}
+static int
+smb_update_shares(void)
+{
+ /* Not implemented */
+ return (0);
+}
+
/*
* Clears a share's SMB options. Used by libshare to
* clean up shares that are about to be free()'d.
@@ -426,17 +411,18 @@ smb_update_shareopts(sa_share_impl_t impl_share, const char *resource,
static void
smb_clear_shareopts(sa_share_impl_t impl_share)
{
- free(FSINFO(impl_share, smb_fstype)->shareopts);
FSINFO(impl_share, smb_fstype)->shareopts = NULL;
}
static const sa_share_ops_t smb_shareops = {
.enable_share = smb_enable_share,
.disable_share = smb_disable_share,
+ .is_shared = smb_is_share_active,
.validate_shareopts = smb_validate_shareopts,
.update_shareopts = smb_update_shareopts,
.clear_shareopts = smb_clear_shareopts,
+ .commit_shares = smb_update_shares,
};
/*
diff --git a/lib/libspl/include/libshare.h b/lib/libspl/include/libshare.h
index 4016ff031..ea53f8c15 100644
--- a/lib/libspl/include/libshare.h
+++ b/lib/libspl/include/libshare.h
@@ -22,14 +22,11 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright (c) 2019, 2020 by Delphix. All rights reserved.
*/
#ifndef _LIBSPL_LIBSHARE_H
#define _LIBSPL_LIBSHARE_H
-typedef void *sa_handle_t; /* opaque handle to access core functions */
-typedef void *sa_group_t;
-typedef void *sa_share_t;
-
/* API Initialization */
#define SA_INIT_SHARE_API 0x0001 /* init share specific interface */
#define SA_INIT_CONTROL_API 0x0002 /* init control specific interface */
@@ -74,23 +71,16 @@ typedef void *sa_share_t;
#define SA_SHARE_EXISTS 33 /* path or file is already shared */
/* initialization */
-extern sa_handle_t sa_init(int);
-extern void sa_fini(sa_handle_t);
extern char *sa_errorstr(int);
/* share control */
-extern sa_share_t sa_find_share(sa_handle_t, char *);
-extern int sa_enable_share(sa_group_t, char *);
-extern int sa_disable_share(sa_share_t, char *);
+extern int sa_enable_share(const char *, const char *, const char *,
+ char *);
+extern int sa_disable_share(const char *, char *);
+extern boolean_t sa_is_shared(const char *, char *);
+extern void sa_commit_shares(const char *);
/* protocol specific interfaces */
-extern int sa_parse_legacy_options(sa_group_t, char *, char *);
-
-/* ZFS functions */
-extern boolean_t sa_needs_refresh(sa_handle_t handle);
-libzfs_handle_t *sa_get_zfs_handle(sa_handle_t handle);
-extern int sa_zfs_process_share(sa_handle_t handle, sa_group_t group,
- sa_share_t share, char *mountpoint, char *proto, zprop_source_t source,
- char *shareopts, char *sourcestr, char *dataset);
+extern int sa_validate_shareopts(char *, char *);
#endif /* _LIBSPL_LIBSHARE_H */
diff --git a/lib/libzfs/Makefile.am b/lib/libzfs/Makefile.am
index 1f7e3c5ee..4154e9fee 100644
--- a/lib/libzfs/Makefile.am
+++ b/lib/libzfs/Makefile.am
@@ -30,7 +30,6 @@ USER_C = \
if BUILD_FREEBSD
USER_C += \
- os/freebsd/libzfs_fsshare.c \
os/freebsd/libzfs_compat.c \
os/freebsd/libzfs_ioctl_compat.c \
os/freebsd/libzfs_zmount.c
@@ -69,14 +68,8 @@ dist_libzfs_la_SOURCES = \
nodist_libzfs_la_SOURCES = \
$(KERNEL_C)
-libzfs_la_LIBADD =
-
-if BUILD_LINUX
-libzfs_la_LIBADD += \
- $(abs_top_builddir)/lib/libshare/libshare.la
-endif
-
-libzfs_la_LIBADD += \
+libzfs_la_LIBADD = \
+ $(abs_top_builddir)/lib/libshare/libshare.la \
$(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
$(abs_top_builddir)/lib/libnvpair/libnvpair.la \
$(abs_top_builddir)/lib/libuutil/libuutil.la
diff --git a/lib/libzfs/libzfs_changelist.c b/lib/libzfs/libzfs_changelist.c
index 72f641056..fec2fd5f2 100644
--- a/lib/libzfs/libzfs_changelist.c
+++ b/lib/libzfs/libzfs_changelist.c
@@ -24,7 +24,7 @@
* Use is subject to license terms.
*
* Portions Copyright 2007 Ramprakash Jelari
- * Copyright (c) 2014, 2015 by Delphix. All rights reserved.
+ * Copyright (c) 2014, 2020 by Delphix. All rights reserved.
* Copyright 2016 Igor Kozhukhov <[email protected]>
* Copyright (c) 2018 Datto Inc.
*/
@@ -98,6 +98,7 @@ changelist_prefix(prop_changelist_t *clp)
prop_changenode_t *cn;
uu_avl_walk_t *walk;
int ret = 0;
+ boolean_t commit_smb_shares = B_FALSE;
if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
clp->cl_prop != ZFS_PROP_SHARESMB)
@@ -135,6 +136,7 @@ changelist_prefix(prop_changelist_t *clp)
break;
case ZFS_PROP_SHARESMB:
(void) zfs_unshare_smb(cn->cn_handle, NULL);
+ commit_smb_shares = B_TRUE;
break;
default:
@@ -143,6 +145,8 @@ changelist_prefix(prop_changelist_t *clp)
}
}
+ if (commit_smb_shares)
+ zfs_commit_smb_shares();
uu_avl_walk_end(walk);
if (ret == -1)
@@ -167,7 +171,8 @@ changelist_postfix(prop_changelist_t *clp)
uu_avl_walk_t *walk;
char shareopts[ZFS_MAXPROPLEN];
int errors = 0;
- libzfs_handle_t *hdl;
+ boolean_t commit_smb_shares = B_FALSE;
+ boolean_t commit_nfs_shares = B_FALSE;
/*
* If we're changing the mountpoint, attempt to destroy the underlying
@@ -183,18 +188,6 @@ changelist_postfix(prop_changelist_t *clp)
remove_mountpoint(cn->cn_handle);
/*
- * It is possible that the changelist_prefix() used libshare
- * to unshare some entries. Since libshare caches data, an
- * attempt to reshare during postfix can fail unless libshare
- * is uninitialized here so that it will reinitialize later.
- */
- if (cn->cn_handle != NULL) {
- hdl = cn->cn_handle->zfs_hdl;
- assert(hdl != NULL);
- zfs_uninit_libshare(hdl);
- }
-
- /*
* We walk the datasets in reverse, because we want to mount any parent
* datasets before mounting the children. We walk all datasets even if
* there are errors.
@@ -260,16 +253,25 @@ changelist_postfix(prop_changelist_t *clp)
* if the filesystem is currently shared, so that we can
* adopt any new options.
*/
- if (sharenfs && mounted)
+ if (sharenfs && mounted) {
errors += zfs_share_nfs(cn->cn_handle);
- else if (cn->cn_shared || clp->cl_waslegacy)
+ commit_nfs_shares = B_TRUE;
+ } else if (cn->cn_shared || clp->cl_waslegacy) {
errors += zfs_unshare_nfs(cn->cn_handle, NULL);
- if (sharesmb && mounted)
+ commit_nfs_shares = B_TRUE;
+ }
+ if (sharesmb && mounted) {
errors += zfs_share_smb(cn->cn_handle);
- else if (cn->cn_shared || clp->cl_waslegacy)
+ commit_smb_shares = B_TRUE;
+ } else if (cn->cn_shared || clp->cl_waslegacy) {
errors += zfs_unshare_smb(cn->cn_handle, NULL);
+ commit_smb_shares = B_TRUE;
+ }
}
-
+ if (commit_nfs_shares)
+ zfs_commit_nfs_shares();
+ if (commit_smb_shares)
+ zfs_commit_smb_shares();
uu_avl_walk_end(walk);
return (errors ? -1 : 0);
@@ -357,6 +359,7 @@ changelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto)
ret = -1;
}
+ zfs_commit_proto(proto);
uu_avl_walk_end(walk);
return (ret);
diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c
index 4252c8cf0..d548cc0ec 100644
--- a/lib/libzfs/libzfs_dataset.c
+++ b/lib/libzfs/libzfs_dataset.c
@@ -22,7 +22,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2019 Joyent, Inc.
- * Copyright (c) 2011, 2018 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
* Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved.
* Copyright (c) 2012 Pawel Jakub Dawidek <[email protected]>.
* Copyright (c) 2013 Martin Matuska. All rights reserved.
@@ -1432,49 +1432,14 @@ badlabel:
else
proto = PROTO_NFS;
- /*
- * Must be an valid sharing protocol
- * option string so init the libshare
- * in order to enable the parser and
- * then parse the options. We use the
- * control API since we don't care about
- * the current configuration and don't
- * want the overhead of loading it
- * until we actually do something.
- */
-
- if (zfs_init_libshare(hdl,
- SA_INIT_CONTROL_API) != SA_OK) {
- /*
- * An error occurred so we can't do
- * anything
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' cannot be set: problem "
- "in share initialization"),
- propname);
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- goto error;
- }
-
if (zfs_parse_options(strval, proto) != SA_OK) {
- /*
- * There was an error in parsing so
- * deal with it by issuing an error
- * message and leaving after
- * uninitializing the libshare
- * interface.
- */
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"'%s' cannot be set to invalid "
"options"), propname);
(void) zfs_error(hdl, EZFS_BADPROP,
errbuf);
- zfs_uninit_libshare(hdl);
goto error;
}
- zfs_uninit_libshare(hdl);
}
break;
@@ -3589,6 +3554,7 @@ create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
zfs_close(h);
}
+ zfs_commit_all_shares();
return (0);
diff --git a/lib/libzfs/libzfs_mount.c b/lib/libzfs/libzfs_mount.c
index b257b6511..6d11016bc 100644
--- a/lib/libzfs/libzfs_mount.c
+++ b/lib/libzfs/libzfs_mount.c
@@ -22,7 +22,7 @@
/*
* 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 (c) 2014, 2020 by Delphix. All rights reserved.
* Copyright 2016 Igor Kozhukhov <[email protected]>
* Copyright 2017 RackTop Systems.
* Copyright (c) 2018 Datto Inc.
@@ -593,10 +593,12 @@ zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
free(mntpt);
return (-1);
}
+ zfs_commit_all_shares();
if (unmount_one(hdl, mntpt, flags) != 0) {
free(mntpt);
(void) zfs_shareall(zhp);
+ zfs_commit_all_shares();
return (-1);
}
@@ -669,6 +671,94 @@ zfs_is_shared(zfs_handle_t *zhp)
return (rc ? B_TRUE : B_FALSE);
}
+/*
+ * Unshare a filesystem by mountpoint.
+ */
+int
+unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint,
+ zfs_share_proto_t proto)
+{
+ int err;
+
+ err = sa_disable_share(mountpoint, 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)));
+ }
+ return (0);
+}
+
+/*
+ * Query libshare for the given mountpoint and protocol, returning
+ * a zfs_share_type_t value.
+ */
+zfs_share_type_t
+is_shared(const char *mountpoint, zfs_share_proto_t proto)
+{
+ if (sa_is_shared(mountpoint, proto_table[proto].p_name)) {
+ switch (proto) {
+ case PROTO_NFS:
+ return (SHARED_NFS);
+ case PROTO_SMB:
+ return (SHARED_SMB);
+ default:
+ return (SHARED_NOT_SHARED);
+ }
+ }
+ return (SHARED_NOT_SHARED);
+}
+
+/*
+ * 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];
+ zfs_share_proto_t *curr_proto;
+ zprop_source_t sourcetype;
+ int err = 0;
+
+ 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;
+
+ /*
+ * 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;
+
+ err = sa_enable_share(zfs_get_name(zhp), mountpoint, shareopts,
+ proto_table[*curr_proto].p_name);
+ if (err != SA_OK) {
+ return (zfs_error_fmt(zhp->zfs_hdl,
+ proto_table[*curr_proto].p_share_err,
+ dgettext(TEXT_DOMAIN, "cannot share '%s: %s'"),
+ zfs_get_name(zhp), sa_errorstr(err)));
+ }
+
+ }
+ return (0);
+}
+
int
zfs_share(zfs_handle_t *zhp)
{
@@ -695,7 +785,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_impl(zhp->zfs_hdl, mountpoint, proto))
+ if ((rc = is_shared(mountpoint, proto))
!= SHARED_NOT_SHARED) {
if (where != NULL)
*where = mountpoint;
@@ -723,21 +813,6 @@ zfs_is_shared_smb(zfs_handle_t *zhp, char **where)
}
/*
- * zfs_uninit_libshare(zhandle)
- *
- * Uninitialize the libshare API if it hasn't already been
- * uninitialized. It is OK to call multiple times.
- */
-void
-zfs_uninit_libshare(libzfs_handle_t *zhandle)
-{
- if (zhandle != NULL && zhandle->libzfs_sharehdl != NULL) {
- sa_fini(zhandle->libzfs_sharehdl);
- zhandle->libzfs_sharehdl = NULL;
- }
-}
-
-/*
* zfs_parse_options(options, proto)
*
* Call the legacy parse interface to get the protocol specific
@@ -746,8 +821,45 @@ zfs_uninit_libshare(libzfs_handle_t *zhandle)
int
zfs_parse_options(char *options, zfs_share_proto_t proto)
{
- return (sa_parse_legacy_options(NULL, options,
- proto_table[proto].p_name));
+ return (sa_validate_shareopts(options, proto_table[proto].p_name));
+}
+
+void
+zfs_commit_proto(zfs_share_proto_t *proto)
+{
+ zfs_share_proto_t *curr_proto;
+ for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) {
+ sa_commit_shares(proto_table[*curr_proto].p_name);
+ }
+}
+
+void
+zfs_commit_nfs_shares(void)
+{
+ zfs_commit_proto(nfs_only);
+}
+
+void
+zfs_commit_smb_shares(void)
+{
+ zfs_commit_proto(smb_only);
+}
+
+void
+zfs_commit_all_shares(void)
+{
+ zfs_commit_proto(share_all_proto);
+}
+
+void
+zfs_commit_shares(const char *proto)
+{
+ if (proto == NULL)
+ zfs_commit_proto(share_all_proto);
+ else if (strcmp(proto, "nfs") == 0)
+ zfs_commit_proto(nfs_only);
+ else if (strcmp(proto, "smb") == 0)
+ zfs_commit_proto(smb_only);
}
int
@@ -793,12 +905,13 @@ zfs_unshare_proto(zfs_handle_t *zhp, const char *mountpoint,
for (curr_proto = proto; *curr_proto != PROTO_END;
curr_proto++) {
- if (is_shared_impl(hdl, mntpt, *curr_proto) &&
- unshare_one(hdl, zhp->zfs_name,
- mntpt, *curr_proto) != 0) {
- if (mntpt != NULL)
- free(mntpt);
- return (-1);
+ if (is_shared(mntpt, *curr_proto)) {
+ if (unshare_one(hdl, zhp->zfs_name,
+ mntpt, *curr_proto) != 0) {
+ if (mntpt != NULL)
+ free(mntpt);
+ return (-1);
+ }
}
}
}
@@ -1338,6 +1451,8 @@ zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
zfs_share_one, &ms, B_FALSE);
if (ms.ms_mntstatus != 0)
ret = ms.ms_mntstatus;
+ else
+ zfs_commit_all_shares();
out:
for (int i = 0; i < cb.cb_used; i++)
@@ -1463,12 +1578,13 @@ 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_impl(hdl, mountpoints[i], *curr_proto) &&
+ if (is_shared(mountpoints[i], *curr_proto) &&
unshare_one(hdl, mountpoints[i],
mountpoints[i], *curr_proto) != 0)
goto out;
}
}
+ zfs_commit_all_shares();
/*
* Now unmount everything, removing the underlying directories as
diff --git a/lib/libzfs/libzfs_util.c b/lib/libzfs/libzfs_util.c
index 2f4aaed32..1fd9d400d 100644
--- a/lib/libzfs/libzfs_util.c
+++ b/lib/libzfs/libzfs_util.c
@@ -22,7 +22,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2020 Joyent, Inc. All rights reserved.
- * Copyright (c) 2011, 2018 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
* Copyright 2016 Igor Kozhukhov <[email protected]>
* Copyright (c) 2017 Datto Inc.
*/
@@ -1024,13 +1024,9 @@ libzfs_init(void)
return (NULL);
}
- hdl->libzfs_sharetab = fopen(ZFS_SHARETAB, "r");
-
if (libzfs_core_init() != 0) {
(void) close(hdl->libzfs_fd);
(void) fclose(hdl->libzfs_mnttab);
- if (hdl->libzfs_sharetab)
- (void) fclose(hdl->libzfs_sharetab);
free(hdl);
return (NULL);
}
@@ -1074,9 +1070,6 @@ libzfs_fini(libzfs_handle_t *hdl)
#else
(void) fclose(hdl->libzfs_mnttab);
#endif
- if (hdl->libzfs_sharetab)
- (void) fclose(hdl->libzfs_sharetab);
- zfs_uninit_libshare(hdl);
zpool_free_handles(hdl);
namespace_clear(hdl);
libzfs_mnttab_fini(hdl);
diff --git a/lib/libzfs/os/freebsd/libzfs_fsshare.c b/lib/libzfs/os/freebsd/libzfs_fsshare.c
deleted file mode 100644
index 9074c33e0..000000000
--- a/lib/libzfs/os/freebsd/libzfs_fsshare.c
+++ /dev/null
@@ -1,406 +0,0 @@
-/*
- * Copyright (c) 2007 Pawel Jakub Dawidek <[email protected]>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/param.h>
-#include <sys/vfs.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <libutil.h>
-#include <signal.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <libintl.h>
-
-#include "libzfs_impl.h"
-
-#define _PATH_MOUNTDPID "/var/run/mountd.pid"
-#define FILE_HEADER "# !!! DO NOT EDIT THIS FILE MANUALLY !!!\n\n"
-#define OPTSSIZE 1024
-#define MAXLINESIZE (PATH_MAX + OPTSSIZE)
-
-
-void
-sa_fini(sa_handle_t handle)
-{
-}
-
-int
-sa_parse_legacy_options(sa_group_t group, char *options, char *proto)
-{
- return (SA_OK);
-}
-
-
-int
-zfs_init_libshare(libzfs_handle_t *zhandle, int service)
-{
- return (SA_OK);
-}
-
-/*
- * 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;
- 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;
-
- if (*curr_proto != PROTO_NFS) {
- fprintf(stderr, "Unsupported share protocol: %d.\n",
- *curr_proto);
- continue;
- }
-
- if (strcmp(shareopts, "on") == 0)
- err = fsshare(ZFS_EXPORTS_PATH, mountpoint, "");
- else
- err = fsshare(ZFS_EXPORTS_PATH, mountpoint, shareopts);
- if (err != 0) {
- (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)
-{
- int err;
-
- if (proto != PROTO_NFS) {
- fprintf(stderr, "No SMB support in FreeBSD yet.\n");
- return (EOPNOTSUPP);
- }
-
- err = fsunshare(ZFS_EXPORTS_PATH, mountpoint);
- if (err != 0) {
- zfs_error_aux(hdl, "%s", strerror(err));
- return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED,
- dgettext(TEXT_DOMAIN,
- "cannot unshare '%s'"), name));
- }
- return (0);
-}
-
-zfs_share_type_t
-is_shared_impl(libzfs_handle_t *hdl, const char *mountpoint,
- zfs_share_proto_t proto)
-{
- char buf[MAXPATHLEN], *tab;
-
- if (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) {
- if (proto == PROTO_NFS)
- return (SHARED_NFS);
- }
- }
-
- return (SHARED_NOT_SHARED);
-}
-
-static void
-restart_mountd(void)
-{
- struct pidfh *pfh;
- pid_t mountdpid;
-
- pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &mountdpid);
- if (pfh != NULL) {
- /* Mountd is not running. */
- pidfile_remove(pfh);
- return;
- }
- if (errno != EEXIST) {
- /* Cannot open pidfile for some reason. */
- return;
- }
- /* We have mountd(8) PID in mountdpid variable. */
- kill(mountdpid, SIGHUP);
-}
-
-/*
- * Read one line from a file. Skip comments, empty lines and a line with a
- * mountpoint specified in the 'skip' argument.
- */
-static char *
-zgetline(FILE *fd, const char *skip)
-{
- static char line[MAXLINESIZE];
- size_t len, skiplen = 0;
- char *s, last;
-
- if (skip != NULL)
- skiplen = strlen(skip);
- for (;;) {
- s = fgets(line, sizeof (line), fd);
- if (s == NULL)
- return (NULL);
- /* Skip empty lines and comments. */
- if (line[0] == '\n' || line[0] == '#')
- continue;
- len = strlen(line);
- if (line[len - 1] == '\n')
- line[len - 1] = '\0';
- last = line[skiplen];
- /* Skip the given mountpoint. */
- if (skip != NULL && strncmp(skip, line, skiplen) == 0 &&
- (last == '\t' || last == ' ' || last == '\0')) {
- continue;
- }
- break;
- }
- return (line);
-}
-/* BEGIN CSTYLED */
-/*
- * Function translate options to a format acceptable by exports(5), eg.
- *
- * -ro -network=192.168.0.0 -mask=255.255.255.0 -maproot=0 freefall.freebsd.org 69.147.83.54
- *
- * Accepted input formats:
- *
- * ro,network=192.168.0.0,mask=255.255.255.0,maproot=0,freefall.freebsd.org
- * ro network=192.168.0.0 mask=255.255.255.0 maproot=0 freefall.freebsd.org
- * -ro,-network=192.168.0.0,-mask=255.255.255.0,-maproot=0,freefall.freebsd.org
- * -ro -network=192.168.0.0 -mask=255.255.255.0 -maproot=0 freefall.freebsd.org
- *
- * Recognized keywords:
- *
- * ro, maproot, mapall, mask, network, sec, alldirs, public, webnfs, index, quiet
- *
- */
-/* END CSTYLED */
-
-static const char *known_opts[] = { "ro", "maproot", "mapall", "mask",
- "network", "sec", "alldirs", "public", "webnfs", "index", "quiet",
- NULL };
-static char *
-translate_opts(const char *shareopts)
-{
- static char newopts[OPTSSIZE];
- char oldopts[OPTSSIZE];
- char *o, *s = NULL;
- unsigned int i;
- size_t len;
-
- strlcpy(oldopts, shareopts, sizeof (oldopts));
- newopts[0] = '\0';
- s = oldopts;
- while ((o = strsep(&s, "-, ")) != NULL) {
- if (o[0] == '\0')
- continue;
- for (i = 0; known_opts[i] != NULL; i++) {
- len = strlen(known_opts[i]);
- if (strncmp(known_opts[i], o, len) == 0 &&
- (o[len] == '\0' || o[len] == '=')) {
- strlcat(newopts, "-", sizeof (newopts));
- break;
- }
- }
- strlcat(newopts, o, sizeof (newopts));
- strlcat(newopts, " ", sizeof (newopts));
- }
- return (newopts);
-}
-
-static int
-fsshare_main(const char *file, const char *mountpoint, const char *shareopts,
- int share)
-{
- char tmpfile[PATH_MAX];
- char *line;
- FILE *newfd, *oldfd;
- int fd, error;
-
- newfd = oldfd = NULL;
- error = 0;
-
- /*
- * Create temporary file in the same directory, so we can atomically
- * rename it.
- */
- if (strlcpy(tmpfile, file, sizeof (tmpfile)) >= sizeof (tmpfile))
- return (ENAMETOOLONG);
- if (strlcat(tmpfile, ".XXXXXXXX", sizeof (tmpfile)) >= sizeof (tmpfile))
- return (ENAMETOOLONG);
- fd = mkstemp(tmpfile);
- if (fd == -1)
- return (errno);
- /*
- * File name is random, so we don't really need file lock now, but it
- * will be needed after rename(2).
- */
- error = flock(fd, LOCK_EX);
- assert(error == 0 || (error == -1 && errno == EOPNOTSUPP));
- newfd = fdopen(fd, "r+");
- assert(newfd != NULL);
- /* Open old exports file. */
- oldfd = fopen(file, "r");
- if (oldfd == NULL) {
- if (share) {
- if (errno != ENOENT) {
- error = errno;
- goto out;
- }
- } else {
- /* If there is no exports file, ignore the error. */
- if (errno == ENOENT)
- errno = 0;
- error = errno;
- goto out;
- }
- } else {
- error = flock(fileno(oldfd), LOCK_EX);
- assert(error == 0 || (error == -1 && errno == EOPNOTSUPP));
- error = 0;
- }
-
- /* Place big, fat warning at the beginning of the file. */
- fprintf(newfd, "%s", FILE_HEADER);
- while (oldfd != NULL && (line = zgetline(oldfd, mountpoint)) != NULL)
- fprintf(newfd, "%s\n", line);
- if (oldfd != NULL && ferror(oldfd) != 0) {
- error = ferror(oldfd);
- goto out;
- }
- if (ferror(newfd) != 0) {
- error = ferror(newfd);
- goto out;
- }
- if (share) {
- fprintf(newfd, "%s\t%s\n", mountpoint,
- translate_opts(shareopts));
- }
-
-out:
- if (error != 0)
- unlink(tmpfile);
- else {
- if (rename(tmpfile, file) == -1) {
- error = errno;
- unlink(tmpfile);
- } else {
- fflush(newfd);
- /*
- * Send SIGHUP to mountd, but unlock exports file later.
- */
- restart_mountd();
- }
- }
- if (oldfd != NULL) {
- flock(fileno(oldfd), LOCK_UN);
- fclose(oldfd);
- }
- if (newfd != NULL) {
- flock(fileno(newfd), LOCK_UN);
- fclose(newfd);
- }
- return (error);
-}
-
-/*
- * Add the given mountpoint to the given exports file.
- */
-int
-fsshare(const char *file, const char *mountpoint, const char *shareopts)
-{
-
- return (fsshare_main(file, mountpoint, shareopts, 1));
-}
-
-/*
- * Remove the given mountpoint from the given exports file.
- */
-int
-fsunshare(const char *file, const char *mountpoint)
-{
-
- return (fsshare_main(file, mountpoint, NULL, 0));
-}
diff --git a/lib/libzfs/os/linux/libzfs_mount_os.c b/lib/libzfs/os/linux/libzfs_mount_os.c
index 7be85097d..92052f28f 100644
--- a/lib/libzfs/os/linux/libzfs_mount_os.c
+++ b/lib/libzfs/os/linux/libzfs_mount_os.c
@@ -22,7 +22,7 @@
/*
* 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 (c) 2014, 2020 by Delphix. All rights reserved.
* Copyright 2016 Igor Kozhukhov <[email protected]>
* Copyright 2017 RackTop Systems.
* Copyright (c) 2018 Datto Inc.
@@ -50,239 +50,6 @@
#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);
-}
-
-
#define ZS_COMMENT 0x00000000 /* comment */
#define ZS_ZFSUTIL 0x00000001 /* caller is zfs(8) */
diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run
index f6478dd0d..61c095290 100644
--- a/tests/runfiles/common.run
+++ b/tests/runfiles/common.run
@@ -258,9 +258,8 @@ tags = ['functional', 'cli_root', 'zfs_set']
[tests/functional/cli_root/zfs_share]
tests = ['zfs_share_001_pos', 'zfs_share_002_pos', 'zfs_share_003_pos',
- 'zfs_share_004_pos', 'zfs_share_005_pos', 'zfs_share_006_pos',
- 'zfs_share_007_neg', 'zfs_share_008_neg', 'zfs_share_009_neg',
- 'zfs_share_010_neg', 'zfs_share_011_pos']
+ 'zfs_share_004_pos', 'zfs_share_006_pos', 'zfs_share_008_neg',
+ 'zfs_share_010_neg', 'zfs_share_011_pos', 'zfs_share_concurrent_shares']
tags = ['functional', 'cli_root', 'zfs_share']
[tests/functional/cli_root/zfs_snapshot]
diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run
index 5b22b7fda..36981bbb0 100644
--- a/tests/runfiles/linux.run
+++ b/tests/runfiles/linux.run
@@ -51,6 +51,10 @@ tags = ['functional', 'cli_root', 'zfs']
tests = ['zfs_mount_006_pos', 'zfs_mount_008_pos', 'zfs_multi_mount']
tags = ['functional', 'cli_root', 'zfs_mount']
+[tests/functional/cli_root/zfs_share:Linux]
+tests = ['zfs_share_005_pos', 'zfs_share_007_neg', 'zfs_share_009_neg']
+tags = ['functional', 'cli_root', 'zfs_share']
+
[tests/functional/cli_root/zfs_sysfs:Linux]
tests = ['zfeature_set_unsupported', 'zfs_get_unsupported',
'zfs_set_unsupported', 'zfs_sysfs_live', 'zpool_get_unsupported',
diff --git a/tests/test-runner/bin/zts-report.py b/tests/test-runner/bin/zts-report.py
index 4a9d08752..78ce291b7 100755
--- a/tests/test-runner/bin/zts-report.py
+++ b/tests/test-runner/bin/zts-report.py
@@ -244,6 +244,9 @@ if sys.platform.startswith('freebsd'):
maybe.update({
'cli_root/zfs_copies/zfs_copies_002_pos': ['FAIL', known_reason],
'cli_root/zfs_inherit/zfs_inherit_001_neg': ['FAIL', known_reason],
+ 'cli_root/zfs_share/zfs_share_011_pos': ['FAIL', known_reason],
+ 'cli_root/zfs_share/zfs_share_concurrent_shares':
+ ['FAIL', known_reason],
'delegate/zfs_allow_003_pos': ['FAIL', known_reason],
'removal/removal_condense_export': ['FAIL', known_reason],
'removal/removal_with_export': ['FAIL', known_reason],
diff --git a/tests/test-runner/include/logapi.shlib b/tests/test-runner/include/logapi.shlib
index 334a04532..aa6e7c0f6 100644
--- a/tests/test-runner/include/logapi.shlib
+++ b/tests/test-runner/include/logapi.shlib
@@ -23,7 +23,7 @@
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# Copyright (c) 2012, 2016 by Delphix. All rights reserved.
+# Copyright (c) 2012, 2020 by Delphix. All rights reserved.
#
. ${STF_TOOLS}/include/stf.shlib
@@ -420,6 +420,11 @@ function log_other
_endlog $STF_OTHER "$@"
}
+function set_main_pid
+{
+ _MAINPID=$1
+}
+
#
# Internal functions
#
@@ -454,6 +459,15 @@ function _endlog
shift
(( ${#@} > 0 )) && _printline "$@"
+ #
+ # If we're running in a subshell then just exit and let
+ # the parent handle the failures
+ #
+ if [[ -n "$_MAINPID" && $$ != "$_MAINPID" ]]; then
+ log_note "subshell exited: "$_MAINPID
+ exit $exitcode
+ fi
+
if [[ $exitcode == $STF_FAIL ]] ; then
_execute_testfail_callbacks
fi
diff --git a/tests/zfs-tests/include/libtest.shlib b/tests/zfs-tests/include/libtest.shlib
index 5e07cda4d..1618c92bd 100644
--- a/tests/zfs-tests/include/libtest.shlib
+++ b/tests/zfs-tests/include/libtest.shlib
@@ -21,7 +21,7 @@
#
# Copyright (c) 2009, Sun Microsystems Inc. All rights reserved.
-# Copyright (c) 2012, 2018, Delphix. All rights reserved.
+# Copyright (c) 2012, 2020, Delphix. All rights reserved.
# Copyright (c) 2017, Tim Chase. All rights reserved.
# Copyright (c) 2017, Nexenta Systems Inc. All rights reserved.
# Copyright (c) 2017, Lawrence Livermore National Security LLC.
@@ -1373,6 +1373,80 @@ function is_shared
esac
}
+function is_exported_illumos
+{
+ typeset fs=$1
+ typeset mtpt
+
+ for mtpt in `awk '{print $1}' /etc/dfs/sharetab` ; do
+ if [[ $mtpt == $fs ]] ; then
+ return 0
+ fi
+ done
+
+ return 1
+}
+
+function is_exported_freebsd
+{
+ typeset fs=$1
+ typeset mtpt
+
+ for mtpt in `awk '{print $1}' /etc/zfs/exports` ; do
+ if [[ $mtpt == $fs ]] ; then
+ return 0
+ fi
+ done
+
+ return 1
+}
+
+function is_exported_linux
+{
+ typeset fs=$1
+ typeset mtpt
+
+ for mtpt in `awk '{print $1}' /etc/exports.d/zfs.exports` ; do
+ if [[ $mtpt == $fs ]] ; then
+ return 0
+ fi
+ done
+
+ return 1
+}
+
+#
+# Given a mountpoint, or a dataset name, determine if it is exported via
+# the os-specific NFS exports file.
+#
+# Returns 0 if exported, 1 otherwise.
+#
+function is_exported
+{
+ typeset fs=$1
+ typeset mtpt
+
+ if [[ $fs != "/"* ]] ; then
+ if datasetnonexists "$fs" ; then
+ return 1
+ else
+ mtpt=$(get_prop mountpoint "$fs")
+ case $mtpt in
+ none|legacy|-) return 1
+ ;;
+ *) fs=$mtpt
+ ;;
+ esac
+ fi
+ fi
+
+ case $(uname) in
+ FreeBSD) is_exported_freebsd "$fs" ;;
+ Linux) is_exported_linux "$fs" ;;
+ *) is_exported_illumos "$fs" ;;
+ esac
+}
+
#
# Given a dataset name determine if it is shared via SMB.
#
@@ -1397,7 +1471,7 @@ function is_shared_smb
done
return 1
else
- log_unsupported "Currently unsupported by the test framework"
+ log_note "Currently unsupported by the test framework"
return 1
fi
}
@@ -1445,7 +1519,7 @@ function unshare_fs #fs
is_shared $fs || is_shared_smb $fs
if (($? == 0)); then
- log_must zfs unshare $fs
+ zfs unshare $fs || log_fail "zfs unshare $fs failed"
fi
return 0
@@ -1523,6 +1597,21 @@ function showshares_smb
return 0
}
+function check_nfs
+{
+ if is_linux; then
+ share -s
+ elif is_freebsd; then
+ showmount -e
+ else
+ log_unsupported "Unknown platform"
+ fi
+
+ if [[ $? -ne 0 ]]; then
+ log_unsupported "The NFS utilities are not installed"
+ fi
+}
+
#
# Check NFS server status and trigger it online.
#
@@ -1535,7 +1624,7 @@ function setup_nfs_server
return
fi
- if is_linux || is_freebsd; then
+ if is_linux; then
#
# Re-synchronize /var/lib/nfs/etab with /etc/exports and
# /etc/exports.d./* to provide a clean test environment.
@@ -1544,6 +1633,11 @@ function setup_nfs_server
log_note "NFS server must be started prior to running ZTS."
return
+ elif is_freebsd; then
+ kill -s HUP $(cat /var/run/mountd.pid)
+
+ log_note "NFS server must be started prior to running ZTS."
+ return
fi
typeset nfs_fmri="svc:/network/nfs/server:default"
@@ -4078,3 +4172,23 @@ function get_arcstat # stat
;;
esac
}
+
+#
+# Given an array of pids, wait until all processes
+# have completed and check their return status.
+#
+function wait_for_children #children
+{
+ rv=0
+ children=("$@")
+ for child in "${children[@]}"
+ do
+ child_exit=0
+ wait ${child} || child_exit=$?
+ if [ $child_exit -ne 0 ]; then
+ echo "child ${child} failed with ${child_exit}"
+ rv=1
+ fi
+ done
+ return $rv
+}
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_share/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zfs_share/Makefile.am
index e20014656..8628b17c4 100644
--- a/tests/zfs-tests/tests/functional/cli_root/zfs_share/Makefile.am
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_share/Makefile.am
@@ -12,7 +12,8 @@ dist_pkgdata_SCRIPTS = \
zfs_share_008_neg.ksh \
zfs_share_009_neg.ksh \
zfs_share_010_neg.ksh \
- zfs_share_011_pos.ksh
+ zfs_share_011_pos.ksh \
+ zfs_share_concurrent_shares.ksh
dist_pkgdata_DATA = \
zfs_share.cfg
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_share/setup.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_share/setup.ksh
index 29f38e802..1601087f7 100755
--- a/tests/zfs-tests/tests/functional/cli_root/zfs_share/setup.ksh
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_share/setup.ksh
@@ -27,10 +27,7 @@
. $STF_SUITE/include/libtest.shlib
-share -s
-if [ $? -ne 0 ]; then
- log_unsupported "The NFS utilities are not installed"
-fi
+check_nfs
# Make sure NFS server is running before testing.
setup_nfs_server
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_share/zfs_share_001_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_share/zfs_share_001_pos.ksh
index a2c06e0b3..fefeb1b1c 100755
--- a/tests/zfs-tests/tests/functional/cli_root/zfs_share/zfs_share_001_pos.ksh
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_share/zfs_share_001_pos.ksh
@@ -26,7 +26,7 @@
#
#
-# Copyright (c) 2016 by Delphix. All rights reserved.
+# Copyright (c) 2016, 2020 by Delphix. All rights reserved.
#
. $STF_SUITE/include/libtest.shlib
@@ -71,6 +71,8 @@ function cleanup
if snapexists "$TESTPOOL/$TESTFS@snapshot"; then
log_must zfs destroy -f $TESTPOOL/$TESTFS@snapshot
fi
+
+ log_must zfs share -a
}
@@ -138,11 +140,20 @@ done
#
log_must zfs share -a
+#
+# We need to unset __ZFS_POOL_EXCLUDE so that we include all file systems
+# in the os-specific zfs exports file. This will be reset by the next test.
+#
+unset __ZFS_POOL_EXCLUDE
+
i=0
while (( i < ${#fs[*]} )); do
is_shared ${fs[i]} || \
log_fail "File system ${fs[i]} is not shared (share -a)"
+ is_exported ${fs[i]} || \
+ log_fail "File system ${fs[i]} is not exported (share -a)"
+
((i = i + 2))
done
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_share/zfs_share_concurrent_shares.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_share/zfs_share_concurrent_shares.ksh
new file mode 100755
index 000000000..bc45820a1
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_share/zfs_share_concurrent_shares.ksh
@@ -0,0 +1,201 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2020 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# Verify that 'zfs set sharenfs=on', 'zfs share', and 'zfs unshare' can
+# run concurrently. The test creates 300 filesystem and 300 threads.
+# Each thread will run through the test strategy in parallel.
+#
+# STRATEGY:
+# 1. Verify that the file system is not shared.
+# 2. Enable the 'sharenfs' property
+# 3. Invoke 'zfs unshare' and verify filesystem is no longer shared
+# 4. Invoke 'zfs share'.
+# 4. Verify that the file system is shared.
+# 5. Verify that a shared filesystem cannot be shared again.
+# 6. Verify that share -a succeeds.
+#
+
+verify_runnable "global"
+
+function cleanup
+{
+ wait
+ for fs in $(seq 0 100)
+ do
+ log_must zfs set sharenfs=off $TESTPOOL/$TESTFS1/$fs
+ log_must zfs set sharenfs=off $TESTPOOL/$TESTFS2/$fs
+ log_must zfs set sharenfs=off $TESTPOOL/$TESTFS3/$fs
+ unshare_fs $TESTPOOL/$TESTFS1/$fs
+ unshare_fs $TESTPOOL/$TESTFS2/$fs
+ unshare_fs $TESTPOOL/$TESTFS3/$fs
+
+ if mounted $TESTPOOL/$TESTFS1/$fs; then
+ log_must zfs unmount $TESTPOOL/$TESTFS1/$fs
+ fi
+ if mounted $TESTPOOL/$TESTFS2/$fs; then
+ log_must zfs unmount $TESTPOOL/$TESTFS2/$fs
+ fi
+ if mounted $TESTPOOL/$TESTFS3/$fs; then
+ log_must zfs unmount $TESTPOOL/$TESTFS3/$fs
+ fi
+
+ datasetexists $TESTPOOL/$TESTFS1/$fs && \
+ log_must zfs destroy -f $TESTPOOL/$TESTFS1/$fs
+ datasetexists $TESTPOOL/$TESTFS2/$fs && \
+ log_must zfs destroy -f $TESTPOOL/$TESTFS2/$fs
+ datasetexists $TESTPOOL/$TESTFS3/$fs && \
+ log_must zfs destroy -f $TESTPOOL/$TESTFS3/$fs
+ done
+
+ log_must zfs share -a
+}
+
+function create_filesystems
+{
+ for fs in $(seq 0 100)
+ do
+ log_must zfs create -p $TESTPOOL/$TESTFS1/$fs
+ log_must zfs create -p $TESTPOOL/$TESTFS2/$fs
+ log_must zfs create -p $TESTPOOL/$TESTFS3/$fs
+ done
+}
+
+#
+# Main test routine.
+#
+# Given a file system this routine will attempt
+# share the mountpoint and then verify it has been shared.
+#
+function test_share # filesystem
+{
+ typeset filesystem=$1
+ typeset mntp=$(get_prop mountpoint $filesystem)
+
+ not_shared $mntp || \
+ log_fail "File system $filesystem is already shared."
+
+ zfs set sharenfs=on $filesystem || \
+ log_fail "zfs set sharenfs=on $filesystem failed."
+ is_shared $mntp || \
+ log_fail "File system $filesystem is not shared (set sharenfs)."
+
+ #
+ # Verify 'zfs share' works as well.
+ #
+ zfs unshare $filesystem || \
+ log_fail "zfs unshare $filesystem failed."
+ is_shared $mntp && \
+ log_fail "File system $filesystem is still shared."
+
+ zfs share $filesystem || \
+ log_fail "zfs share $filesystem failed."
+ is_shared $mntp || \
+ log_fail "file system $filesystem is not shared (zfs share)."
+
+ #log_note "Sharing a shared file system fails."
+ zfs share $filesystem && \
+ log_fail "zfs share $filesystem did not fail"
+ return 0
+}
+
+#
+# Set the main process id so that we know to capture
+# failures from child processes and allow the parent process
+# to report the failure.
+#
+set_main_pid $$
+log_assert "Verify that 'zfs share' succeeds as root."
+log_onexit cleanup
+
+create_filesystems
+
+child_pids=()
+for fs in $(seq 0 100)
+do
+ test_share $TESTPOOL/$TESTFS1/$fs &
+ child_pids+=($!)
+ log_note "$TESTPOOL/$TESTFS1/$fs ==> $!"
+ test_share $TESTPOOL/$TESTFS2/$fs &
+ child_pids+=($!)
+ log_note "$TESTPOOL/$TESTFS2/$fs ==> $!"
+ test_share $TESTPOOL/$TESTFS3/$fs &
+ child_pids+=($!)
+ log_note "$TESTPOOL/$TESTFS3/$fs ==> $!"
+done
+wait_for_children "${child_pids[@]}" ||
+ log_fail "multithreaded share test failed"
+
+log_note "Verify 'zfs share -a' succeeds."
+
+#
+# Unshare each of the file systems.
+#
+child_pids=()
+for fs in $(seq 0 100)
+do
+ unshare_fs $TESTPOOL/$TESTFS1/$fs &
+ child_pids+=($!)
+ unshare_fs $TESTPOOL/$TESTFS2/$fs &
+ child_pids+=($!)
+ unshare_fs $TESTPOOL/$TESTFS3/$fs &
+ child_pids+=($!)
+done
+wait_for_children "${child_pids[@]}" ||
+ log_fail "multithreaded unshare failed"
+
+#
+# Try a zfs share -a and verify all file systems are shared.
+#
+log_must zfs share -a
+
+#
+# We need to unset __ZFS_POOL_EXCLUDE so that we include all file systems
+# in the os-specific zfs exports file. This will be reset by the next test.
+#
+unset __ZFS_POOL_EXCLUDE
+
+for fs in $(seq 0 100)
+do
+ is_shared $TESTPOOL/$TESTFS1/$fs || \
+ log_fail "File system $TESTPOOL/$TESTFS1/$fs is not shared"
+ is_shared $TESTPOOL/$TESTFS2/$fs || \
+ log_fail "File system $TESTPOOL/$TESTFS2/$fs is not shared"
+ is_shared $TESTPOOL/$TESTFS3/$fs || \
+ log_fail "File system $TESTPOOL/$TESTFS3/$fs is not shared"
+
+ is_exported $TESTPOOL/$TESTFS1/$fs || \
+ log_fail "File system $TESTPOOL/$TESTFS1/$fs is not exported"
+ is_exported $TESTPOOL/$TESTFS2/$fs || \
+ log_fail "File system $TESTPOOL/$TESTFS2/$fs is not exported"
+ is_exported $TESTPOOL/$TESTFS3/$fs || \
+ log_fail "File system $TESTPOOL/$TESTFS3/$fs is not exported"
+done
+
+log_pass "'zfs share [ -a ] <filesystem>' succeeds as root."