16185db85Sdougm /*
26185db85Sdougm  * CDDL HEADER START
36185db85Sdougm  *
46185db85Sdougm  * The contents of this file are subject to the terms of the
56185db85Sdougm  * Common Development and Distribution License (the "License").
66185db85Sdougm  * You may not use this file except in compliance with the License.
76185db85Sdougm  *
86185db85Sdougm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96185db85Sdougm  * or http://www.opensolaris.org/os/licensing.
106185db85Sdougm  * See the License for the specific language governing permissions
116185db85Sdougm  * and limitations under the License.
126185db85Sdougm  *
136185db85Sdougm  * When distributing Covered Code, include this CDDL HEADER in each
146185db85Sdougm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156185db85Sdougm  * If applicable, add the following below this CDDL HEADER, with the
166185db85Sdougm  * fields enclosed by brackets "[]" replaced with your own identifying
176185db85Sdougm  * information: Portions Copyright [yyyy] [name of copyright owner]
186185db85Sdougm  *
196185db85Sdougm  * CDDL HEADER END
206185db85Sdougm  */
216185db85Sdougm 
226185db85Sdougm /*
23148c5f43SAlan Wright  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
246185db85Sdougm  */
25*0dfe541eSEvan Layton 
26c3f7431dSDan Kruchinin /*
2795e79c0bSDaniel Hoffman  * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
28ff524b23SAndrew Stormont  * Copyright 2017 RackTop Systems.
29*0dfe541eSEvan Layton  * Copyright 2019 Nexenta Systems, Inc.
30c3f7431dSDan Kruchinin  */
316185db85Sdougm 
32a1ef5d63Smarks #include <stdio.h>
336185db85Sdougm #include <libzfs.h>
346185db85Sdougm #include <string.h>
35a3351425Sdougm #include <strings.h>
36a63214d6SBill Krier #include <errno.h>
37*0dfe541eSEvan Layton #include <zone.h>
386185db85Sdougm #include <libshare.h>
396185db85Sdougm #include "libshare_impl.h"
401cea05afSdougm #include <libintl.h>
41a1ef5d63Smarks #include <sys/mnttab.h>
42a1ef5d63Smarks #include <sys/mntent.h>
4395e79c0bSDaniel Hoffman #include <assert.h>
446185db85Sdougm 
45da6c28aaSamw extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *, uint64_t);
466185db85Sdougm extern sa_group_t _sa_create_zfs_group(sa_group_t, char *);
476185db85Sdougm extern char *sa_fstype(char *);
486185db85Sdougm extern void set_node_attr(void *, char *, char *);
496185db85Sdougm extern int sa_is_share(void *);
505b6e0c46Sdougm extern void sa_update_sharetab_ts(sa_handle_t);
511cea05afSdougm 
521cea05afSdougm /*
531cea05afSdougm  * File system specific code for ZFS. The original code was stolen
541cea05afSdougm  * from the "zfs" command and modified to better suit this library's
551cea05afSdougm  * usage.
561cea05afSdougm  */
571cea05afSdougm 
581cea05afSdougm typedef struct get_all_cbdata {
591cea05afSdougm 	zfs_handle_t	**cb_handles;
601cea05afSdougm 	size_t		cb_alloc;
611cea05afSdougm 	size_t		cb_used;
62a3351425Sdougm 	uint_t		cb_types;
631cea05afSdougm } get_all_cbdata_t;
641cea05afSdougm 
651cea05afSdougm /*
66549ec3ffSdougm  * sa_zfs_init(impl_handle)
671cea05afSdougm  *
68549ec3ffSdougm  * Initialize an access handle into libzfs.  The handle needs to stay
69549ec3ffSdougm  * around until sa_zfs_fini() in order to maintain the cache of
70549ec3ffSdougm  * mounts.
711cea05afSdougm  */
721cea05afSdougm 
7357b448deSdougm int
sa_zfs_init(sa_handle_impl_t impl_handle)74549ec3ffSdougm sa_zfs_init(sa_handle_impl_t impl_handle)
751cea05afSdougm {
76549ec3ffSdougm 	impl_handle->zfs_libhandle = libzfs_init();
7757b448deSdougm 	if (impl_handle->zfs_libhandle != NULL) {
7857b448deSdougm 		libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE);
7957b448deSdougm 		return (B_TRUE);
8057b448deSdougm 	}
8157b448deSdougm 	return (B_FALSE);
821cea05afSdougm }
831cea05afSdougm 
841cea05afSdougm /*
85549ec3ffSdougm  * sa_zfs_fini(impl_handle)
861cea05afSdougm  *
871cea05afSdougm  * cleanup data structures and the libzfs handle used for accessing
881cea05afSdougm  * zfs file share info.
891cea05afSdougm  */
901cea05afSdougm 
911cea05afSdougm void
sa_zfs_fini(sa_handle_impl_t impl_handle)92549ec3ffSdougm sa_zfs_fini(sa_handle_impl_t impl_handle)
931cea05afSdougm {
94549ec3ffSdougm 	if (impl_handle->zfs_libhandle != NULL) {
9557b448deSdougm 		if (impl_handle->zfs_list != NULL) {
96a3351425Sdougm 			zfs_handle_t **zhp = impl_handle->zfs_list;
97a3351425Sdougm 			size_t i;
98a3351425Sdougm 
9957b448deSdougm 			/*
100a3351425Sdougm 			 * Contents of zfs_list need to be freed so we
101a3351425Sdougm 			 * don't lose ZFS handles.
10257b448deSdougm 			 */
103a3351425Sdougm 			for (i = 0; i < impl_handle->zfs_list_count; i++) {
104a3351425Sdougm 				zfs_close(zhp[i]);
105a3351425Sdougm 			}
10657b448deSdougm 			free(impl_handle->zfs_list);
10757b448deSdougm 			impl_handle->zfs_list = NULL;
10857b448deSdougm 			impl_handle->zfs_list_count = 0;
10957b448deSdougm 		}
110a3351425Sdougm 
111a3351425Sdougm 		libzfs_fini(impl_handle->zfs_libhandle);
112a3351425Sdougm 		impl_handle->zfs_libhandle = NULL;
1131cea05afSdougm 	}
1141cea05afSdougm }
1151cea05afSdougm 
1161cea05afSdougm /*
1171cea05afSdougm  * get_one_filesystem(zfs_handle_t, data)
1181cea05afSdougm  *
119da6c28aaSamw  * an iterator function called while iterating through the ZFS
1201cea05afSdougm  * root. It accumulates into an array of file system handles that can
1211cea05afSdougm  * be used to derive info about those file systems.
122a3351425Sdougm  *
123a3351425Sdougm  * Note that as this function is called, we close all zhp handles that
124a3351425Sdougm  * are not going to be places into the cp_handles list. We don't want
125a3351425Sdougm  * to close the ones we are keeping, but all others would be leaked if
126a3351425Sdougm  * not closed here.
1271cea05afSdougm  */
1281cea05afSdougm 
1291cea05afSdougm static int
get_one_filesystem(zfs_handle_t * zhp,void * data)1301cea05afSdougm get_one_filesystem(zfs_handle_t *zhp, void *data)
1311cea05afSdougm {
1321cea05afSdougm 	get_all_cbdata_t *cbp = data;
133a3351425Sdougm 	zfs_type_t type = zfs_get_type(zhp);
1341cea05afSdougm 
1351cea05afSdougm 	/*
136a3351425Sdougm 	 * Interate over any nested datasets.
1371cea05afSdougm 	 */
138a3351425Sdougm 	if (type == ZFS_TYPE_FILESYSTEM &&
139a3351425Sdougm 	    zfs_iter_filesystems(zhp, get_one_filesystem, data) != 0) {
140a3351425Sdougm 		zfs_close(zhp);
141a3351425Sdougm 		return (1);
142a3351425Sdougm 	}
143a3351425Sdougm 
144a3351425Sdougm 	/*
145a3351425Sdougm 	 * Skip any datasets whose type does not match.
146a3351425Sdougm 	 */
147a3351425Sdougm 	if ((type & cbp->cb_types) == 0) {
1481cea05afSdougm 		zfs_close(zhp);
1491cea05afSdougm 		return (0);
1501cea05afSdougm 	}
1511cea05afSdougm 
1521cea05afSdougm 	if (cbp->cb_alloc == cbp->cb_used) {
1531cea05afSdougm 		zfs_handle_t **handles;
1541cea05afSdougm 
1551cea05afSdougm 		if (cbp->cb_alloc == 0)
1561cea05afSdougm 			cbp->cb_alloc = 64;
1571cea05afSdougm 		else
1581cea05afSdougm 			cbp->cb_alloc *= 2;
1591cea05afSdougm 
160a3351425Sdougm 		handles = (zfs_handle_t **)calloc(1,
161a3351425Sdougm 		    cbp->cb_alloc * sizeof (void *));
162a3351425Sdougm 
1631cea05afSdougm 		if (handles == NULL) {
164a3351425Sdougm 			zfs_close(zhp);
16557b448deSdougm 			return (0);
1661cea05afSdougm 		}
1671cea05afSdougm 		if (cbp->cb_handles) {
168a3351425Sdougm 			bcopy(cbp->cb_handles, handles,
1691cea05afSdougm 			    cbp->cb_used * sizeof (void *));
1701cea05afSdougm 			free(cbp->cb_handles);
1711cea05afSdougm 		}
1721cea05afSdougm 
1731cea05afSdougm 		cbp->cb_handles = handles;
1741cea05afSdougm 	}
1751cea05afSdougm 
1761cea05afSdougm 	cbp->cb_handles[cbp->cb_used++] = zhp;
1771cea05afSdougm 
178a3351425Sdougm 	return (0);
1791cea05afSdougm }
1801cea05afSdougm 
1811cea05afSdougm /*
1821cea05afSdougm  * get_all_filesystems(zfs_handle_t ***fslist, size_t *count)
1831cea05afSdougm  *
1841cea05afSdougm  * iterate through all ZFS file systems starting at the root. Returns
1851cea05afSdougm  * a count and an array of handle pointers. Allocating is only done
1861cea05afSdougm  * once. The caller does not need to free since it will be done at
1871cea05afSdougm  * sa_zfs_fini() time.
1881cea05afSdougm  */
1891cea05afSdougm 
1901cea05afSdougm static void
get_all_filesystems(sa_handle_impl_t impl_handle,zfs_handle_t *** fslist,size_t * count)191549ec3ffSdougm get_all_filesystems(sa_handle_impl_t impl_handle,
19295e79c0bSDaniel Hoffman     zfs_handle_t ***fslist, size_t *count)
1931cea05afSdougm {
1941cea05afSdougm 	get_all_cbdata_t cb = { 0 };
195a3351425Sdougm 	cb.cb_types = ZFS_TYPE_FILESYSTEM;
1961cea05afSdougm 
197549ec3ffSdougm 	if (impl_handle->zfs_list != NULL) {
19857b448deSdougm 		*fslist = impl_handle->zfs_list;
19957b448deSdougm 		*count = impl_handle->zfs_list_count;
20057b448deSdougm 		return;
2011cea05afSdougm 	}
2021cea05afSdougm 
203549ec3ffSdougm 	(void) zfs_iter_root(impl_handle->zfs_libhandle,
20457b448deSdougm 	    get_one_filesystem, &cb);
2051cea05afSdougm 
206549ec3ffSdougm 	impl_handle->zfs_list = *fslist = cb.cb_handles;
207549ec3ffSdougm 	impl_handle->zfs_list_count = *count = cb.cb_used;
2081cea05afSdougm }
2091cea05afSdougm 
2106185db85Sdougm /*
2111cea05afSdougm  * mountpoint_compare(a, b)
2121cea05afSdougm  *
2131cea05afSdougm  * compares the mountpoint on two zfs file systems handles.
2141cea05afSdougm  * returns values following strcmp() model.
2156185db85Sdougm  */
2166185db85Sdougm 
2171cea05afSdougm static int
mountpoint_compare(const void * a,const void * b)2181cea05afSdougm mountpoint_compare(const void *a, const void *b)
2191cea05afSdougm {
2201cea05afSdougm 	zfs_handle_t **za = (zfs_handle_t **)a;
2211cea05afSdougm 	zfs_handle_t **zb = (zfs_handle_t **)b;
2221cea05afSdougm 	char mounta[MAXPATHLEN];
2231cea05afSdougm 	char mountb[MAXPATHLEN];
2241cea05afSdougm 
2251cea05afSdougm 	verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
2261cea05afSdougm 	    sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
2271cea05afSdougm 	verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
2281cea05afSdougm 	    sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
2291cea05afSdougm 
2301cea05afSdougm 	return (strcmp(mounta, mountb));
2311cea05afSdougm }
2321cea05afSdougm 
233a1ef5d63Smarks /*
234743a77edSAlan Wright  * return legacy mountpoint.  Caller provides space for mountpoint and
235743a77edSAlan Wright  * dataset.
236a1ef5d63Smarks  */
237a1ef5d63Smarks int
get_legacy_mountpoint(const char * path,char * dataset,size_t dlen,char * mountpoint,size_t mlen)23895e79c0bSDaniel Hoffman get_legacy_mountpoint(const char *path, char *dataset, size_t dlen,
239743a77edSAlan Wright     char *mountpoint, size_t mlen)
240a1ef5d63Smarks {
241a1ef5d63Smarks 	FILE *fp;
242a1ef5d63Smarks 	struct mnttab entry;
243*0dfe541eSEvan Layton 	int rc = 1;
244a1ef5d63Smarks 
245a1ef5d63Smarks 	if ((fp = fopen(MNTTAB, "r")) == NULL) {
246a1ef5d63Smarks 		return (1);
247a1ef5d63Smarks 	}
248a1ef5d63Smarks 
249a1ef5d63Smarks 	while (getmntent(fp, &entry) == 0) {
250a1ef5d63Smarks 
251a1ef5d63Smarks 		if (entry.mnt_fstype == NULL ||
252a1ef5d63Smarks 		    strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
253a1ef5d63Smarks 			continue;
254a1ef5d63Smarks 
255a1ef5d63Smarks 		if (strcmp(entry.mnt_mountp, path) == 0) {
256743a77edSAlan Wright 			if (mlen > 0)
257743a77edSAlan Wright 				(void) strlcpy(mountpoint, entry.mnt_mountp,
258743a77edSAlan Wright 				    mlen);
259743a77edSAlan Wright 			if (dlen > 0)
260743a77edSAlan Wright 				(void) strlcpy(dataset, entry.mnt_special,
261743a77edSAlan Wright 				    dlen);
262*0dfe541eSEvan Layton 			rc = 0;
263743a77edSAlan Wright 			break;
264a1ef5d63Smarks 		}
265a1ef5d63Smarks 	}
266a1ef5d63Smarks 	(void) fclose(fp);
267*0dfe541eSEvan Layton 	return (rc);
268a1ef5d63Smarks }
269a1ef5d63Smarks 
27095e79c0bSDaniel Hoffman 
2718a981c33SDaniel Hoffman /*
2728a981c33SDaniel Hoffman  * Verifies that a specific zfs filesystem handle meets the criteria necessary
2738a981c33SDaniel Hoffman  * to be used by libshare operations. See get_zfs_dataset.
2748a981c33SDaniel Hoffman  */
27595e79c0bSDaniel Hoffman static char *
verify_zfs_handle(zfs_handle_t * hdl,const char * path,boolean_t search_mnttab)27695e79c0bSDaniel Hoffman verify_zfs_handle(zfs_handle_t *hdl, const char *path, boolean_t search_mnttab)
27795e79c0bSDaniel Hoffman {
27895e79c0bSDaniel Hoffman 	char mountpoint[ZFS_MAXPROPLEN];
27995e79c0bSDaniel Hoffman 	char canmount[ZFS_MAXPROPLEN] = { 0 };
28095e79c0bSDaniel Hoffman 	/* must have a mountpoint */
28195e79c0bSDaniel Hoffman 	if (zfs_prop_get(hdl, ZFS_PROP_MOUNTPOINT, mountpoint,
28295e79c0bSDaniel Hoffman 	    sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
28395e79c0bSDaniel Hoffman 		/* no mountpoint */
28495e79c0bSDaniel Hoffman 		return (NULL);
28595e79c0bSDaniel Hoffman 	}
28695e79c0bSDaniel Hoffman 
28795e79c0bSDaniel Hoffman 	/* mountpoint must be a path */
28895e79c0bSDaniel Hoffman 	if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
28995e79c0bSDaniel Hoffman 	    strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
29095e79c0bSDaniel Hoffman 		/*
29195e79c0bSDaniel Hoffman 		 * Search mmttab for mountpoint and get dataset.
29295e79c0bSDaniel Hoffman 		 */
29395e79c0bSDaniel Hoffman 
29495e79c0bSDaniel Hoffman 		if (search_mnttab == B_TRUE &&
29595e79c0bSDaniel Hoffman 		    get_legacy_mountpoint(path, mountpoint,
29695e79c0bSDaniel Hoffman 		    sizeof (mountpoint), NULL, 0) == 0) {
29795e79c0bSDaniel Hoffman 			return (strdup(mountpoint));
29895e79c0bSDaniel Hoffman 		}
29995e79c0bSDaniel Hoffman 		return (NULL);
30095e79c0bSDaniel Hoffman 	}
30195e79c0bSDaniel Hoffman 
30295e79c0bSDaniel Hoffman 	/* canmount must be set */
30395e79c0bSDaniel Hoffman 	if (zfs_prop_get(hdl, ZFS_PROP_CANMOUNT, canmount,
30495e79c0bSDaniel Hoffman 	    sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 ||
30595e79c0bSDaniel Hoffman 	    strcmp(canmount, "off") == 0)
30695e79c0bSDaniel Hoffman 		return (NULL);
30795e79c0bSDaniel Hoffman 
30895e79c0bSDaniel Hoffman 	/*
30995e79c0bSDaniel Hoffman 	 * have a mountable handle but want to skip those marked none
31095e79c0bSDaniel Hoffman 	 * and legacy
31195e79c0bSDaniel Hoffman 	 */
31295e79c0bSDaniel Hoffman 	if (strcmp(mountpoint, path) == 0) {
31395e79c0bSDaniel Hoffman 		return (strdup((char *)zfs_get_name(hdl)));
31495e79c0bSDaniel Hoffman 	}
31595e79c0bSDaniel Hoffman 
31695e79c0bSDaniel Hoffman 	return (NULL);
31795e79c0bSDaniel Hoffman }
31895e79c0bSDaniel Hoffman 
3196185db85Sdougm /*
320549ec3ffSdougm  * get_zfs_dataset(impl_handle, path)
3216185db85Sdougm  *
3226185db85Sdougm  * get the name of the ZFS dataset the path is equivalent to.  The
3236185db85Sdougm  * dataset name is used for get/set of ZFS properties since libzfs
3246185db85Sdougm  * requires a dataset to do a zfs_open().
3256185db85Sdougm  */
3266185db85Sdougm 
3276185db85Sdougm static char *
get_zfs_dataset(sa_handle_impl_t impl_handle,char * path,boolean_t search_mnttab)328a1ef5d63Smarks get_zfs_dataset(sa_handle_impl_t impl_handle, char *path,
329a1ef5d63Smarks     boolean_t search_mnttab)
3306185db85Sdougm {
3311cea05afSdougm 	size_t i, count = 0;
3321cea05afSdougm 	zfs_handle_t **zlist;
33395e79c0bSDaniel Hoffman 	char *cutpath;
33495e79c0bSDaniel Hoffman 	zfs_handle_t *handle_from_path;
33595e79c0bSDaniel Hoffman 	char *ret = NULL;
3366185db85Sdougm 
33795e79c0bSDaniel Hoffman 	/*
33895e79c0bSDaniel Hoffman 	 * First we optimistically assume that the mount path for the filesystem
33995e79c0bSDaniel Hoffman 	 * is the same as the name of the filesystem (minus some number of
34095e79c0bSDaniel Hoffman 	 * leading slashes). If this is true, then zfs_open should properly open
34195e79c0bSDaniel Hoffman 	 * the filesystem. We duplicate the error checking done later in the
34295e79c0bSDaniel Hoffman 	 * function for consistency. If anything fails, we resort to the
34395e79c0bSDaniel Hoffman 	 * (extremely slow) search of all the filesystems.
34495e79c0bSDaniel Hoffman 	 */
34595e79c0bSDaniel Hoffman 	cutpath = path + strspn(path, "/");
34695e79c0bSDaniel Hoffman 
34795e79c0bSDaniel Hoffman 	assert(impl_handle->zfs_libhandle != NULL);
348ff524b23SAndrew Stormont 	libzfs_print_on_error(impl_handle->zfs_libhandle, B_FALSE);
3498a981c33SDaniel Hoffman 	handle_from_path = zfs_open(impl_handle->zfs_libhandle, cutpath,
3508a981c33SDaniel Hoffman 	    ZFS_TYPE_FILESYSTEM);
3518a981c33SDaniel Hoffman 	libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE);
3528a981c33SDaniel Hoffman 	if (handle_from_path != NULL) {
3538a981c33SDaniel Hoffman 		ret = verify_zfs_handle(handle_from_path, path, search_mnttab);
3548a981c33SDaniel Hoffman 		zfs_close(handle_from_path);
3558a981c33SDaniel Hoffman 		if (ret != NULL) {
35695e79c0bSDaniel Hoffman 			return (ret);
357ff524b23SAndrew Stormont 		}
358ff524b23SAndrew Stormont 	}
35995e79c0bSDaniel Hoffman 	/*
36095e79c0bSDaniel Hoffman 	 * Couldn't find a filesystem optimistically, check all the handles we
36195e79c0bSDaniel Hoffman 	 * can.
36295e79c0bSDaniel Hoffman 	 */
363549ec3ffSdougm 	get_all_filesystems(impl_handle, &zlist, &count);
3641cea05afSdougm 	for (i = 0; i < count; i++) {
36595e79c0bSDaniel Hoffman 		assert(zlist[i]);
36695e79c0bSDaniel Hoffman 		if ((ret = verify_zfs_handle(zlist[i], path,
36795e79c0bSDaniel Hoffman 		    search_mnttab)) != NULL)
36895e79c0bSDaniel Hoffman 			return (ret);
3696185db85Sdougm 	}
3701cea05afSdougm 
37195e79c0bSDaniel Hoffman 	/* Couldn't find a matching dataset */
37295e79c0bSDaniel Hoffman 	return (NULL);
3736185db85Sdougm }
3746185db85Sdougm 
3756185db85Sdougm /*
3766185db85Sdougm  * get_zfs_property(dataset, property)
3776185db85Sdougm  *
3786185db85Sdougm  * Get the file system property specified from the ZFS dataset.
3796185db85Sdougm  */
3806185db85Sdougm 
3816185db85Sdougm static char *
get_zfs_property(char * dataset,zfs_prop_t property)3826185db85Sdougm get_zfs_property(char *dataset, zfs_prop_t property)
3836185db85Sdougm {
3846185db85Sdougm 	zfs_handle_t *handle = NULL;
3856185db85Sdougm 	char shareopts[ZFS_MAXPROPLEN];
3866185db85Sdougm 	libzfs_handle_t *libhandle;
3876185db85Sdougm 
3886185db85Sdougm 	libhandle = libzfs_init();
3896185db85Sdougm 	if (libhandle != NULL) {
39057b448deSdougm 		handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
39157b448deSdougm 		if (handle != NULL) {
39257b448deSdougm 			if (zfs_prop_get(handle, property, shareopts,
39357b448deSdougm 			    sizeof (shareopts), NULL, NULL, 0,
39457b448deSdougm 			    B_FALSE) == 0) {
39557b448deSdougm 				zfs_close(handle);
39657b448deSdougm 				libzfs_fini(libhandle);
39757b448deSdougm 				return (strdup(shareopts));
39857b448deSdougm 			}
39957b448deSdougm 			zfs_close(handle);
4006185db85Sdougm 		}
40157b448deSdougm 		libzfs_fini(libhandle);
4026185db85Sdougm 	}
4036185db85Sdougm 	return (NULL);
4046185db85Sdougm }
4056185db85Sdougm 
4066185db85Sdougm /*
407549ec3ffSdougm  * sa_zfs_is_shared(handle, path)
4086185db85Sdougm  *
4096185db85Sdougm  * Check to see if the ZFS path provided has the sharenfs option set
4106185db85Sdougm  * or not.
4116185db85Sdougm  */
4126185db85Sdougm 
4136185db85Sdougm int
sa_zfs_is_shared(sa_handle_t sahandle,char * path)414549ec3ffSdougm sa_zfs_is_shared(sa_handle_t sahandle, char *path)
4156185db85Sdougm {
4166185db85Sdougm 	int ret = 0;
4176185db85Sdougm 	char *dataset;
4186185db85Sdougm 	zfs_handle_t *handle = NULL;
4196185db85Sdougm 	char shareopts[ZFS_MAXPROPLEN];
4206185db85Sdougm 	libzfs_handle_t *libhandle;
4216185db85Sdougm 
422a1ef5d63Smarks 	dataset = get_zfs_dataset((sa_handle_t)sahandle, path, B_FALSE);
4236185db85Sdougm 	if (dataset != NULL) {
42457b448deSdougm 		libhandle = libzfs_init();
42557b448deSdougm 		if (libhandle != NULL) {
42657b448deSdougm 			handle = zfs_open(libhandle, dataset,
42757b448deSdougm 			    ZFS_TYPE_FILESYSTEM);
42857b448deSdougm 			if (handle != NULL) {
42957b448deSdougm 				if (zfs_prop_get(handle, ZFS_PROP_SHARENFS,
43057b448deSdougm 				    shareopts, sizeof (shareopts), NULL, NULL,
43157b448deSdougm 				    0, B_FALSE) == 0 &&
43257b448deSdougm 				    strcmp(shareopts, "off") != 0) {
43357b448deSdougm 					ret = 1; /* it is shared */
43457b448deSdougm 				}
43557b448deSdougm 				zfs_close(handle);
43657b448deSdougm 			}
43757b448deSdougm 			libzfs_fini(libhandle);
4386185db85Sdougm 		}
43957b448deSdougm 		free(dataset);
4406185db85Sdougm 	}
4416185db85Sdougm 	return (ret);
4426185db85Sdougm }
4436185db85Sdougm 
4446185db85Sdougm /*
445da6c28aaSamw  * find_or_create_group(handle, groupname, proto, *err)
4466185db85Sdougm  *
4476185db85Sdougm  * While walking the ZFS tree, we need to add shares to a defined
4486185db85Sdougm  * group. If the group doesn't exist, create it first, making sure it
4496185db85Sdougm  * is marked as a ZFS group.
4506185db85Sdougm  *
45193a6f655Sdougm  * Note that all ZFS shares are in a subgroup of the top level group
45293a6f655Sdougm  * called "zfs".
4536185db85Sdougm  */
4546185db85Sdougm 
4556185db85Sdougm static sa_group_t
find_or_create_group(sa_handle_t handle,char * groupname,char * proto,int * err)456549ec3ffSdougm find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err)
4576185db85Sdougm {
4586185db85Sdougm 	sa_group_t group;
4596185db85Sdougm 	sa_optionset_t optionset;
4606185db85Sdougm 	int ret = SA_OK;
4616185db85Sdougm 
4626185db85Sdougm 	/*
4636185db85Sdougm 	 * we check to see if the "zfs" group exists. Since this
4646185db85Sdougm 	 * should be the top level group, we don't want the
4656185db85Sdougm 	 * parent. This is to make sure the zfs group has been created
4666185db85Sdougm 	 * and to created if it hasn't been.
4676185db85Sdougm 	 */
468549ec3ffSdougm 	group = sa_get_group(handle, groupname);
4696185db85Sdougm 	if (group == NULL) {
47057b448deSdougm 		group = sa_create_group(handle, groupname, &ret);
47193a6f655Sdougm 
47257b448deSdougm 		/* make sure this is flagged as a ZFS group */
47357b448deSdougm 		if (group != NULL)
47457b448deSdougm 			ret = sa_set_group_attr(group, "zfs", "true");
4756185db85Sdougm 	}
4766185db85Sdougm 	if (group != NULL) {
47757b448deSdougm 		if (proto != NULL) {
47857b448deSdougm 			optionset = sa_get_optionset(group, proto);
479da6c28aaSamw 			if (optionset == NULL)
48057b448deSdougm 				optionset = sa_create_optionset(group, proto);
4816185db85Sdougm 		}
4826185db85Sdougm 	}
4836185db85Sdougm 	if (err != NULL)
48457b448deSdougm 		*err = ret;
4856185db85Sdougm 	return (group);
4866185db85Sdougm }
4876185db85Sdougm 
48893a6f655Sdougm /*
48993a6f655Sdougm  * find_or_create_zfs_subgroup(groupname, optstring, *err)
49093a6f655Sdougm  *
49193a6f655Sdougm  * ZFS shares will be in a subgroup of the "zfs" master group.  This
49293a6f655Sdougm  * function looks to see if the groupname exists and returns it if it
49393a6f655Sdougm  * does or else creates a new one with the specified name and returns
49493a6f655Sdougm  * that.  The "zfs" group will exist before we get here, but we make
49593a6f655Sdougm  * sure just in case.
49693a6f655Sdougm  *
49793a6f655Sdougm  * err must be a valid pointer.
49893a6f655Sdougm  */
49993a6f655Sdougm 
50093a6f655Sdougm static sa_group_t
find_or_create_zfs_subgroup(sa_handle_t handle,char * groupname,char * proto,char * optstring,int * err)501da6c28aaSamw find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname, char *proto,
502da6c28aaSamw     char *optstring, int *err)
50393a6f655Sdougm {
50493a6f655Sdougm 	sa_group_t group = NULL;
50593a6f655Sdougm 	sa_group_t zfs;
50693a6f655Sdougm 	char *name;
50793a6f655Sdougm 	char *options;
50893a6f655Sdougm 
50993a6f655Sdougm 	/* start with the top-level "zfs" group */
510549ec3ffSdougm 	zfs = sa_get_group(handle, "zfs");
51193a6f655Sdougm 	*err = SA_OK;
51293a6f655Sdougm 	if (zfs != NULL) {
51357b448deSdougm 		for (group = sa_get_sub_group(zfs); group != NULL;
51457b448deSdougm 		    group = sa_get_next_group(group)) {
51557b448deSdougm 			name = sa_get_group_attr(group, "name");
51657b448deSdougm 			if (name != NULL && strcmp(name, groupname) == 0) {
51757b448deSdougm 				/* have the group so break out of here */
51857b448deSdougm 				sa_free_attr_string(name);
51957b448deSdougm 				break;
52057b448deSdougm 			}
52157b448deSdougm 			if (name != NULL)
52257b448deSdougm 				sa_free_attr_string(name);
52393a6f655Sdougm 		}
52457b448deSdougm 
52557b448deSdougm 		if (group == NULL) {
52657b448deSdougm 			/*
5273c484793Sdougm 			 * Need to create the sub-group since it doesn't exist
52857b448deSdougm 			 */
52957b448deSdougm 			group = _sa_create_zfs_group(zfs, groupname);
5303c484793Sdougm 			if (group == NULL) {
5313c484793Sdougm 				*err = SA_NO_MEMORY;
5323c484793Sdougm 				return (NULL);
53357b448deSdougm 			}
5343c484793Sdougm 			set_node_attr(group, "zfs", "true");
5353c484793Sdougm 		}
5363c484793Sdougm 		if (strcmp(optstring, "on") == 0)
5373c484793Sdougm 			optstring = "rw";
5383c484793Sdougm 		options = strdup(optstring);
5393c484793Sdougm 		if (options != NULL) {
5403c484793Sdougm 			*err = sa_parse_legacy_options(group, options,
5413c484793Sdougm 			    proto);
5423c484793Sdougm 			/* If no optionset, add one. */
5433c484793Sdougm 			if (sa_get_optionset(group, proto) == NULL)
5443c484793Sdougm 				(void) sa_create_optionset(group, proto);
545c3f7431dSDan Kruchinin 
546c3f7431dSDan Kruchinin 			/*
547c3f7431dSDan Kruchinin 			 * Do not forget to update an optionset of
548c3f7431dSDan Kruchinin 			 * the parent group so that it contains
549c3f7431dSDan Kruchinin 			 * all protocols its subgroups have.
550c3f7431dSDan Kruchinin 			 */
551c3f7431dSDan Kruchinin 			if (sa_get_optionset(zfs, proto) == NULL)
552c3f7431dSDan Kruchinin 				(void) sa_create_optionset(zfs, proto);
553c3f7431dSDan Kruchinin 
5543c484793Sdougm 			free(options);
5553c484793Sdougm 		} else {
5563c484793Sdougm 			*err = SA_NO_MEMORY;
55793a6f655Sdougm 		}
55893a6f655Sdougm 	}
55993a6f655Sdougm 	return (group);
56093a6f655Sdougm }
56193a6f655Sdougm 
562da6c28aaSamw /*
563da6c28aaSamw  * zfs_construct_resource(share, name, base, dataset)
564da6c28aaSamw  *
565da6c28aaSamw  * Add a resource to the share using name as a template. If name ==
566da6c28aaSamw  * NULL, then construct a name based on the dataset value.
567da6c28aaSamw  * name.
568da6c28aaSamw  */
569da6c28aaSamw static void
zfs_construct_resource(sa_share_t share,char * dataset)570da6c28aaSamw zfs_construct_resource(sa_share_t share, char *dataset)
571da6c28aaSamw {
572da6c28aaSamw 	char buff[SA_MAX_RESOURCE_NAME + 1];
573da6c28aaSamw 	int ret = SA_OK;
574da6c28aaSamw 
575da6c28aaSamw 	(void) snprintf(buff, SA_MAX_RESOURCE_NAME, "%s", dataset);
576da6c28aaSamw 	sa_fix_resource_name(buff);
577da6c28aaSamw 	(void) sa_add_resource(share, buff, SA_SHARE_TRANSIENT, &ret);
578da6c28aaSamw }
579da6c28aaSamw 
58057b448deSdougm /*
58157b448deSdougm  * zfs_inherited(handle, source, sourcestr)
58257b448deSdougm  *
583da6c28aaSamw  * handle case of inherited share{nfs,smb}. Pulled out of sa_get_zfs_shares
58457b448deSdougm  * for readability.
58557b448deSdougm  */
58657b448deSdougm static int
zfs_inherited(sa_handle_t handle,sa_share_t share,char * sourcestr,char * shareopts,char * mountpoint,char * proto,char * dataset)58757b448deSdougm zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr,
588da6c28aaSamw     char *shareopts, char *mountpoint, char *proto, char *dataset)
58957b448deSdougm {
59057b448deSdougm 	int doshopt = 0;
59157b448deSdougm 	int err = SA_OK;
59257b448deSdougm 	sa_group_t group;
593da6c28aaSamw 	sa_resource_t resource;
594da6c28aaSamw 	uint64_t features;
59557b448deSdougm 
59657b448deSdougm 	/*
59757b448deSdougm 	 * Need to find the "real" parent sub-group. It may not be
59857b448deSdougm 	 * mounted, but it was identified in the "sourcestr"
59957b448deSdougm 	 * variable. The real parent not mounted can occur if
60057b448deSdougm 	 * "canmount=off and sharenfs=on".
60157b448deSdougm 	 */
602da6c28aaSamw 	group = find_or_create_zfs_subgroup(handle, sourcestr, proto,
603da6c28aaSamw 	    shareopts, &doshopt);
60457b448deSdougm 	if (group != NULL) {
605da6c28aaSamw 		/*
606da6c28aaSamw 		 * We may need the first share for resource
607da6c28aaSamw 		 * prototype. We only care about it if it has a
608da6c28aaSamw 		 * resource that sets a prefix value.
609da6c28aaSamw 		 */
610da6c28aaSamw 		if (share == NULL)
611