diff options
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." |