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)
611da6c28aaSamw 			share = _sa_add_share(group, mountpoint,
612da6c28aaSamw 			    SA_SHARE_TRANSIENT, &err,
613da6c28aaSamw 			    (uint64_t)SA_FEATURE_NONE);
61457b448deSdougm 		/*
61557b448deSdougm 		 * some options may only be on shares. If the opt
61657b448deSdougm 		 * string contains one of those, we put it just on the
61757b448deSdougm 		 * share.
61857b448deSdougm 		 */
61957b448deSdougm 		if (share != NULL && doshopt == SA_PROP_SHARE_ONLY) {
62057b448deSdougm 			char *options;
62157b448deSdougm 			options = strdup(shareopts);
62257b448deSdougm 			if (options != NULL) {
623da6c28aaSamw 				set_node_attr(share, "dataset", dataset);
62457b448deSdougm 				err = sa_parse_legacy_options(share, options,
625da6c28aaSamw 				    proto);
626da6c28aaSamw 				set_node_attr(share, "dataset", NULL);
62757b448deSdougm 				free(options);
62857b448deSdougm 			}
629da6c28aaSamw 			if (sa_get_optionset(group, proto) == NULL)
630da6c28aaSamw 				(void) sa_create_optionset(group, proto);
631da6c28aaSamw 		}
632da6c28aaSamw 		features = sa_proto_get_featureset(proto);
633da6c28aaSamw 		if (share != NULL && features & SA_FEATURE_RESOURCE) {
634da6c28aaSamw 			/*
635da6c28aaSamw 			 * We have a share and the protocol requires
636da6c28aaSamw 			 * that at least one resource exist (probably
637da6c28aaSamw 			 * SMB). We need to make sure that there is at
638da6c28aaSamw 			 * least one.
639da6c28aaSamw 			 */
640da6c28aaSamw 			resource = sa_get_share_resource(share, NULL);
641da6c28aaSamw 			if (resource == NULL) {
642da6c28aaSamw 				zfs_construct_resource(share, dataset);
643da6c28aaSamw 			}
64457b448deSdougm 		}
64557b448deSdougm 	} else {
64657b448deSdougm 		err = SA_NO_MEMORY;
64757b448deSdougm 	}
64857b448deSdougm 	return (err);
64957b448deSdougm }
65057b448deSdougm 
65157b448deSdougm /*
65297df5ac9Sdougm  * zfs_notinherited(group, share, mountpoint, shareopts, proto, dataset,
65397df5ac9Sdougm  *     grouperr)
65457b448deSdougm  *
65557b448deSdougm  * handle case where this is the top of a sub-group in ZFS. Pulled out
65697df5ac9Sdougm  * of sa_get_zfs_shares for readability. We need the grouperr from the
65797df5ac9Sdougm  * creation of the subgroup to know whether to add the public
65897df5ac9Sdougm  * property, etc. to the specific share.
65957b448deSdougm  */
66057b448deSdougm static int
zfs_notinherited(sa_group_t group,sa_share_t share,char * mountpoint,char * shareopts,char * proto,char * dataset,int grouperr)661da6c28aaSamw zfs_notinherited(sa_group_t group, sa_share_t share, char *mountpoint,
66297df5ac9Sdougm     char *shareopts, char *proto, char *dataset, int grouperr)
66357b448deSdougm {
66457b448deSdougm 	int err = SA_OK;
665da6c28aaSamw 	sa_resource_t resource;
666da6c28aaSamw 	uint64_t features;
66757b448deSdougm 
66857b448deSdougm 	set_node_attr(group, "zfs", "true");
669da6c28aaSamw 	if (share == NULL)
670da6c28aaSamw 		share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT,
671da6c28aaSamw 		    &err, (uint64_t)SA_FEATURE_NONE);
6723c484793Sdougm 
6733c484793Sdougm 	if (err != SA_OK)
6743c484793Sdougm 		return (err);
6753c484793Sdougm 
6763c484793Sdougm 	if (strcmp(shareopts, "on") == 0)
6773c484793Sdougm 		shareopts = "";
6783c484793Sdougm 	if (shareopts != NULL) {
6793c484793Sdougm 		char *options;
6803c484793Sdougm 		if (grouperr == SA_PROP_SHARE_ONLY) {
681da6c28aaSamw 			/*
6823c484793Sdougm 			 * Some properties may only be on shares, but
6833c484793Sdougm 			 * due to the ZFS sub-groups being artificial,
6843c484793Sdougm 			 * we sometimes get this and have to deal with
6853c484793Sdougm 			 * it. We do it by attempting to put it on the
6863c484793Sdougm 			 * share.
687da6c28aaSamw 			 */
6883c484793Sdougm 			options = strdup(shareopts);
6893c484793Sdougm 			if (options != NULL) {
6903c484793Sdougm 				err = sa_parse_legacy_options(share,
6913c484793Sdougm 				    options, proto);
6923c484793Sdougm 				free(options);
693da6c28aaSamw 			}
69457b448deSdougm 		}
6953c484793Sdougm 		/* Unmark the share's changed state */
6963c484793Sdougm 		set_node_attr(share, "changed", NULL);
6973c484793Sdougm 	}
6983c484793Sdougm 	features = sa_proto_get_featureset(proto);
6993c484793Sdougm 	if (share != NULL && features & SA_FEATURE_RESOURCE) {
7003c484793Sdougm 		/*
7013c484793Sdougm 		 * We have a share and the protocol requires that at
7023c484793Sdougm 		 * least one resource exist (probably SMB). We need to
7033c484793Sdougm 		 * make sure that there is at least one.
7043c484793Sdougm 		 */
7053c484793Sdougm 		resource = sa_get_share_resource(share, NULL);
7063c484793Sdougm 		if (resource == NULL) {
7073c484793Sdougm 			zfs_construct_resource(share, dataset);
7083c484793Sdougm 		}
70957b448deSdougm 	}
71057b448deSdougm 	return (err);
71157b448deSdougm }
71257b448deSdougm 
71357b448deSdougm /*
71457b448deSdougm  * zfs_grp_error(err)
71557b448deSdougm  *
71657b448deSdougm  * Print group create error, but only once. If err is 0 do the
71757b448deSdougm  * print else don't.
71857b448deSdougm  */
71957b448deSdougm 
72057b448deSdougm static void
zfs_grp_error(int err)72157b448deSdougm zfs_grp_error(int err)
72257b448deSdougm {
72357b448deSdougm 	if (err == 0) {
72457b448deSdougm 		/* only print error once */
72557b448deSdougm 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
72657b448deSdougm 		    "Cannot create ZFS subgroup during initialization:"
72757b448deSdougm 		    " %s\n"), sa_errorstr(SA_SYSTEM_ERR));
72857b448deSdougm 	}
72957b448deSdougm }
73057b448deSdougm 
731da6c28aaSamw /*
732da6c28aaSamw  * zfs_process_share(handle, share, mountpoint, proto, source,
733da6c28aaSamw  *     shareopts, sourcestr)
734da6c28aaSamw  *
7355b6e0c46Sdougm  * Creates the subgroup, if necessary and adds shares, resources
736da6c28aaSamw  * and properties.
737da6c28aaSamw  */
7385b6e0c46Sdougm 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)7395b6e0c46Sdougm sa_zfs_process_share(sa_handle_t handle, sa_group_t group, sa_share_t share,
740da6c28aaSamw     char *mountpoint, char *proto, zprop_source_t source, char *shareopts,
741da6c28aaSamw     char *sourcestr, char *dataset)
742da6c28aaSamw {
743da6c28aaSamw 	int err = SA_OK;
744da6c28aaSamw 
745da6c28aaSamw 	if (source & ZPROP_SRC_INHERITED) {
746da6c28aaSamw 		err = zfs_inherited(handle, share, sourcestr, shareopts,
747da6c28aaSamw 		    mountpoint, proto, dataset);
748da6c28aaSamw 	} else {
749da6c28aaSamw 		group = find_or_create_zfs_subgroup(handle, dataset, proto,
750da6c28aaSamw 		    shareopts, &err);
751da6c28aaSamw 		if (group == NULL) {
75297df5ac9Sdougm 			static boolean_t reported_error = B_FALSE;
753da6c28aaSamw 			/*
75497df5ac9Sdougm 			 * There is a problem, but we can't do
755da6c28aaSamw 			 * anything about it at this point so we issue
75697df5ac9Sdougm 			 * a warning and move on.
757da6c28aaSamw 			 */
75897df5ac9Sdougm 			zfs_grp_error(reported_error);
75997df5ac9Sdougm 			reported_error = B_TRUE;
760da6c28aaSamw 		}
761da6c28aaSamw 		set_node_attr(group, "zfs", "true");
762da6c28aaSamw 		/*
763da6c28aaSamw 		 * Add share with local opts via zfs_notinherited.
764da6c28aaSamw 		 */
765da6c28aaSamw 		err = zfs_notinherited(group, share, mountpoint, shareopts,
76697df5ac9Sdougm 		    proto, dataset, err);
767da6c28aaSamw 	}
768da6c28aaSamw 	return (err);
769da6c28aaSamw }
770da6c28aaSamw 
7716185db85Sdougm /*
7726185db85Sdougm  * Walk the mnttab for all zfs mounts and determine which are
7736185db85Sdougm  * shared. Find or create the appropriate group/sub-group to contain
7746185db85Sdougm  * the shares.
7756185db85Sdougm  *
7766185db85Sdougm  * All shares are in a sub-group that will hold the properties. This
7776185db85Sdougm  * allows representing the inherited property model.
7783c484793Sdougm  *
7793c484793Sdougm  * One area of complication is if "sharenfs" is set at one level of
7803c484793Sdougm  * the directory tree and "sharesmb" is set at a different level, the
7813c484793Sdougm  * a sub-group must be formed at the lower level for both
7823c484793Sdougm  * protocols. That is the nature of the problem in CR 6667349.
7836185db85Sdougm  */
7848a981c33SDaniel Hoffman static int
sa_get_zfs_share_common(sa_handle_t handle,zfs_handle_t * fs_handle,char * path,sa_group_t zfsgroup)7858a981c33SDaniel Hoffman sa_get_zfs_share_common(sa_handle_t handle, zfs_handle_t *fs_handle, char *path,
7868a981c33SDaniel Hoffman     sa_group_t zfsgroup)
7876185db85Sdougm {
7888a981c33SDaniel Hoffman 	boolean_t smb, nfs;
7898a981c33SDaniel Hoffman 	boolean_t smb_inherited, nfs_inherited;
7903c484793Sdougm 	char nfsshareopts[ZFS_MAXPROPLEN];
7913c484793Sdougm 	char smbshareopts[ZFS_MAXPROPLEN];
7923c484793Sdougm 	char nfssourcestr[ZFS_MAXPROPLEN];
7933c484793Sdougm 	char smbsourcestr[ZFS_MAXPROPLEN];
7941cea05afSdougm 	char mountpoint[ZFS_MAXPROPLEN];
7953c484793Sdougm 	int err = SA_OK;
7968a981c33SDaniel Hoffman 	zprop_source_t source;
7978a981c33SDaniel Hoffman 	sa_share_t share;
7988a981c33SDaniel Hoffman 	char *dataset;
7998a981c33SDaniel Hoffman 
8008a981c33SDaniel Hoffman 	source = ZPROP_SRC_ALL;
8018a981c33SDaniel Hoffman 	/* If no mountpoint, skip. */
8028a981c33SDaniel Hoffman 	if (zfs_prop_get(fs_handle, ZFS_PROP_MOUNTPOINT,
8038a981c33SDaniel Hoffman 	    mountpoint, sizeof (mountpoint), NULL, NULL, 0,
8048a981c33SDaniel Hoffman 	    B_FALSE) != 0)
8058a981c33SDaniel Hoffman 		return (SA_SYSTEM_ERR);
8066185db85Sdougm 
8078a981c33SDaniel Hoffman 	if (path != NULL)
8088a981c33SDaniel Hoffman 		(void) strncpy(path, mountpoint, sizeof (mountpoint));
8096185db85Sdougm 	/*
8108a981c33SDaniel Hoffman 	 * zfs_get_name value must not be freed. It is just a
8118a981c33SDaniel Hoffman 	 * pointer to a value in the handle.
8126185db85Sdougm 	 */
8138a981c33SDaniel Hoffman 	if ((dataset = (char *)zfs_get_name(fs_handle)) == NULL)
81457b448deSdougm 		return (SA_SYSTEM_ERR);
8156185db85Sdougm 
816546405c3Sdougm 	/*
8178a981c33SDaniel Hoffman 	 * only deal with "mounted" file systems since
8188a981c33SDaniel Hoffman 	 * unmounted file systems can't actually be shared.
819546405c3Sdougm 	 */
820546405c3Sdougm 
8218a981c33SDaniel Hoffman 	if (!zfs_is_mounted(fs_handle, NULL))
8228a981c33SDaniel Hoffman 		return (SA_SYSTEM_ERR);
8231cea05afSdougm 
824*0dfe541eSEvan Layton 	/*
825*0dfe541eSEvan Layton 	 * Ignore "zoned" datasets in global zone.
826*0dfe541eSEvan Layton 	 */
827*0dfe541eSEvan Layton 	if (getzoneid() == GLOBAL_ZONEID &&
828*0dfe541eSEvan Layton 	    zfs_prop_get_int(fs_handle, ZFS_PROP_ZONED))
829*0dfe541eSEvan Layton 		return (SA_SYSTEM_ERR);
830*0dfe541eSEvan Layton 
8318a981c33SDaniel Hoffman 	nfs = nfs_inherited = B_FALSE;
83257b448deSdougm 
8338a981c33SDaniel Hoffman 	if (zfs_prop_get(fs_handle, ZFS_PROP_SHARENFS, nfsshareopts,
8348a981c33SDaniel Hoffman 	    sizeof (nfsshareopts), &source, nfssourcestr,
8358a981c33SDaniel Hoffman 	    ZFS_MAXPROPLEN, B_FALSE) == 0 &&
8368a981c33SDaniel Hoffman 	    strcmp(nfsshareopts, "off") != 0) {
8378a981c33SDaniel Hoffman 		if (source & ZPROP_SRC_INHERITED)
8388a981c33SDaniel Hoffman 			nfs_inherited = B_TRUE;
8398a981c33SDaniel Hoffman 		else
8408a981c33SDaniel Hoffman 			nfs = B_TRUE;
8418a981c33SDaniel Hoffman 	}
8423c484793Sdougm 
8438a981c33SDaniel Hoffman 	smb = smb_inherited = B_FALSE;
8448a981c33SDaniel Hoffman 	if (zfs_prop_get(fs_handle, ZFS_PROP_SHARESMB, smbshareopts,
8458a981c33SDaniel Hoffman 	    sizeof (smbshareopts), &source, smbsourcestr,
8468a981c33SDaniel Hoffman 	    ZFS_MAXPROPLEN, B_FALSE) == 0 &&
8478a981c33SDaniel Hoffman 	    strcmp(smbshareopts, "off") != 0) {
8488a981c33SDaniel Hoffman 		if (source & ZPROP_SRC_INHERITED)
8498a981c33SDaniel Hoffman 			smb_inherited = B_TRUE;
8508a981c33SDaniel Hoffman 		else
8518a981c33SDaniel Hoffman 			smb = B_TRUE;
8528a981c33SDaniel Hoffman 	}
8533c484793Sdougm 
8548a981c33SDaniel Hoffman 	/*
8558a981c33SDaniel Hoffman 	 * If the mountpoint is already shared, it must be a
8568a981c33SDaniel Hoffman 	 * non-ZFS share. We want to remove the share from its
8578a981c33SDaniel Hoffman 	 * parent group and reshare it under ZFS.
8588a981c33SDaniel Hoffman 	 */
8598a981c33SDaniel Hoffman 	share = sa_find_share(handle, mountpoint);
8608a981c33SDaniel Hoffman 	if (share != NULL &&
8618a981c33SDaniel Hoffman 	    (nfs || smb || nfs_inherited || smb_inherited)) {
8628a981c33SDaniel Hoffman 		err = sa_remove_share(share);
8638a981c33SDaniel Hoffman 		share = NULL;
8648a981c33SDaniel Hoffman 	}
8653c484793Sdougm 
8668a981c33SDaniel Hoffman 	/*
8678a981c33SDaniel Hoffman 	 * At this point, we have the information needed to
8688a981c33SDaniel Hoffman 	 * determine what to do with the share.
8698a981c33SDaniel Hoffman 	 *
8708a981c33SDaniel Hoffman 	 * If smb or nfs is set, we have a new sub-group.
8718a981c33SDaniel Hoffman 	 * If smb_inherit and/or nfs_inherit is set, then
8728a981c33SDaniel Hoffman 	 * place on an existing sub-group. If both are set,
8738a981c33SDaniel Hoffman 	 * the existing sub-group is the closest up the tree.
8748a981c33SDaniel Hoffman 	 */
8758a981c33SDaniel Hoffman 	if (nfs || smb) {
8763c484793Sdougm 		/*
8778a981c33SDaniel Hoffman 		 * Non-inherited is the straightforward
8788a981c33SDaniel Hoffman 		 * case. sa_zfs_process_share handles it
8798a981c33SDaniel Hoffman 		 * directly. Make sure that if the "other"
8808a981c33SDaniel Hoffman 		 * protocol is inherited, that we treat it as
8818a981c33SDaniel Hoffman 		 * non-inherited as well.
8823c484793Sdougm 		 */
8838a981c33SDaniel Hoffman 		if (nfs || nfs_inherited) {
8848a981c33SDaniel Hoffman 			err = sa_zfs_process_share(handle, zfsgroup,
8858a981c33SDaniel Hoffman 			    share, mountpoint, "nfs",
8868a981c33SDaniel Hoffman 			    0, nfsshareopts,
8878a981c33SDaniel Hoffman 			    nfssourcestr, dataset);
8888a981c33SDaniel Hoffman 			share = sa_find_share(handle, mountpoint);
8893c484793Sdougm 		}
8908a981c33SDaniel Hoffman 		if (smb || smb_inherited) {
8918a981c33SDaniel Hoffman 			err = sa_zfs_process_share(handle, zfsgroup,
8928a981c33SDaniel Hoffman 			    share, mountpoint, "smb",
8938a981c33SDaniel Hoffman 			    0, smbshareopts,
8948a981c33SDaniel Hoffman 			    smbsourcestr, dataset);
8958a981c33SDaniel Hoffman 		}
8968a981c33SDaniel Hoffman 	} else if (nfs_inherited || smb_inherited) {
8978a981c33SDaniel Hoffman 		char *grpdataset;
8983c484793Sdougm 		/*
8998a981c33SDaniel Hoffman 		 * If we only have inherited groups, it is
9008a981c33SDaniel Hoffman 		 * important to find the closer of the two if
9018a981c33SDaniel Hoffman 		 * the protocols are set at different
9028a981c33SDaniel Hoffman 		 * levels. The closest sub-group is the one we
9038a981c33SDaniel Hoffman 		 * want to work with.
9043c484793Sdougm 		 */
9058a981c33SDaniel Hoffman 		if (nfs_inherited && smb_inherited) {
9068a981c33SDaniel Hoffman 			if (strcmp(nfssourcestr, smbsourcestr) <= 0)
9073c484793Sdougm 				grpdataset = nfssourcestr;
9088a981c33SDaniel Hoffman 			else
9093c484793Sdougm 				grpdataset = smbsourcestr;
9108a981c33SDaniel Hoffman 		} else if (nfs_inherited) {
9118a981c33SDaniel Hoffman 			grpdataset = nfssourcestr;
9128a981c33SDaniel Hoffman 		} else if (smb_inherited) {
9138a981c33SDaniel Hoffman 			grpdataset = smbsourcestr;
9148a981c33SDaniel Hoffman 		}
9158a981c33SDaniel Hoffman 		if (nfs_inherited) {
9168a981c33SDaniel Hoffman 			err = sa_zfs_process_share(handle, zfsgroup,
9178a981c33SDaniel Hoffman 			    share, mountpoint, "nfs",
9188a981c33SDaniel Hoffman 			    ZPROP_SRC_INHERITED, nfsshareopts,
9198a981c33SDaniel Hoffman 			    grpdataset, dataset);
9208a981c33SDaniel Hoffman 			share = sa_find_share(handle, mountpoint);
9218a981c33SDaniel Hoffman 		}
9228a981c33SDaniel Hoffman 		if (smb_inherited) {
9238a981c33SDaniel Hoffman 			err = sa_zfs_process_share(handle, zfsgroup,
9248a981c33SDaniel Hoffman 			    share, mountpoint, "smb",
9258a981c33SDaniel Hoffman 			    ZPROP_SRC_INHERITED, smbshareopts,
9268a981c33SDaniel Hoffman 			    grpdataset, dataset);
9276185db85Sdougm 		}
9286185db85Sdougm 	}
9298a981c33SDaniel Hoffman 	return (err);
9308a981c33SDaniel Hoffman }
9318a981c33SDaniel Hoffman 
9328a981c33SDaniel Hoffman /*
9338a981c33SDaniel Hoffman  * Handles preparing generic objects such as the libzfs handle and group for
9348a981c33SDaniel Hoffman  * sa_get_one_zfs_share, sa_get_zfs_share_for_name, and sa_get_zfs_shares.
9358a981c33SDaniel Hoffman  */
9368a981c33SDaniel Hoffman static int
prep_zfs_handle_and_group(sa_handle_t handle,char * groupname,libzfs_handle_t ** zfs_libhandle,sa_group_t * zfsgroup,int * err)9378a981c33SDaniel Hoffman prep_zfs_handle_and_group(sa_handle_t handle, char *groupname,
9388a981c33SDaniel Hoffman     libzfs_handle_t **zfs_libhandle, sa_group_t *zfsgroup, int *err)
9398a981c33SDaniel Hoffman {
9408a981c33SDaniel Hoffman 	/*
9418a981c33SDaniel Hoffman 	 * If we can't access libzfs, don't bother doing anything.
9428a981c33SDaniel Hoffman 	 */
9438a981c33SDaniel Hoffman 	*zfs_libhandle = ((sa_handle_impl_t)handle)->zfs_libhandle;
9448a981c33SDaniel Hoffman 	if (*zfs_libhandle == NULL)
9458a981c33SDaniel Hoffman 		return (SA_SYSTEM_ERR);
9468a981c33SDaniel Hoffman 
9478a981c33SDaniel Hoffman 	*zfsgroup = find_or_create_group(handle, groupname, NULL, err);
9488a981c33SDaniel Hoffman 	return (SA_OK);
9498a981c33SDaniel Hoffman }
9508a981c33SDaniel Hoffman 
9518a981c33SDaniel Hoffman /*
9528a981c33SDaniel Hoffman  * The O.G. zfs share preparation function. This initializes all zfs shares for
9538a981c33SDaniel Hoffman  * use with libshare.
9548a981c33SDaniel Hoffman  */
9558a981c33SDaniel Hoffman int
sa_get_zfs_shares(sa_handle_t handle,char * groupname)9568a981c33SDaniel Hoffman sa_get_zfs_shares(sa_handle_t handle, char *groupname)
9578a981c33SDaniel Hoffman {
9588a981c33SDaniel Hoffman 	sa_group_t zfsgroup;
9598a981c33SDaniel Hoffman 	zfs_handle_t **zlist;
9608a981c33SDaniel Hoffman 	size_t count = 0;
9618a981c33SDaniel Hoffman 	libzfs_handle_t *zfs_libhandle;
9628a981c33SDaniel Hoffman 	int err;
9638a981c33SDaniel Hoffman 
9648a981c33SDaniel Hoffman 	if ((err = prep_zfs_handle_and_group(handle, groupname, &zfs_libhandle,
9658a981c33SDaniel Hoffman 	    &zfsgroup, &err)) != SA_OK) {
9668a981c33SDaniel Hoffman 		return (err);
9678a981c33SDaniel Hoffman 	}
9688a981c33SDaniel Hoffman 	/* Not an error, this could be a legacy condition */
9698a981c33SDaniel Hoffman 	if (zfsgroup == NULL)
9708a981c33SDaniel Hoffman 		return (SA_OK);
9718a981c33SDaniel Hoffman 
9728a981c33SDaniel Hoffman 	/*
9738a981c33SDaniel Hoffman 	 * need to walk the mounted ZFS pools and datasets to
9748a981c33SDaniel Hoffman 	 * find shares that are possible.
9758a981c33SDaniel Hoffman 	 */
9768a981c33SDaniel Hoffman 	get_all_filesystems((sa_handle_impl_t)handle, &zlist, &count);
9778a981c33SDaniel Hoffman 	qsort(zlist, count, sizeof (void *), mountpoint_compare);
9788a981c33SDaniel Hoffman 
9798a981c33SDaniel Hoffman 	for (int i = 0; i < count; i++) {
9808a981c33SDaniel Hoffman 		err = sa_get_zfs_share_common(handle, zlist[i], NULL, zfsgroup);
9818a981c33SDaniel Hoffman 	}
9821cea05afSdougm 	/*
9831cea05afSdougm 	 * Don't need to free the "zlist" variable since it is only a
9841cea05afSdougm 	 * pointer to a cached value that will be freed when
9851cea05afSdougm 	 * sa_fini() is called.
9861cea05afSdougm 	 */
9873c484793Sdougm 	return (err);
9886185db85Sdougm }
9896185db85Sdougm 
990*0dfe541eSEvan Layton /*
991*0dfe541eSEvan Layton  * Initializes shares for only the dataset specified fs_handle.
992*0dfe541eSEvan Layton  * This is used as a performance optimization relative to sa_get_zfs_shares.
993*0dfe541eSEvan Layton  */
994*0dfe541eSEvan Layton int
sa_get_zfs_share(sa_handle_t handle,char * groupname,zfs_handle_t * fs_handle)995*0dfe541eSEvan Layton sa_get_zfs_share(sa_handle_t handle, char *groupname, zfs_handle_t *fs_handle)
996*0dfe541eSEvan Layton {
997*0dfe541eSEvan Layton 	sa_group_t zfsgroup;
998*0dfe541eSEvan Layton 	libzfs_handle_t *zfs_libhandle;
999*0dfe541eSEvan Layton 	int err;
1000*0dfe541eSEvan Layton 
1001*0dfe541eSEvan Layton 	if ((err = prep_zfs_handle_and_group(handle, groupname, &zfs_libhandle,
1002*0dfe541eSEvan Layton 	    &zfsgroup, &err)) != SA_OK) {
1003*0dfe541eSEvan Layton 		return (err);
1004*0dfe541eSEvan Layton 	}
1005*0dfe541eSEvan Layton 	/* Not an error, this could be a legacy condition */
1006*0dfe541eSEvan Layton 	if (zfsgroup == NULL)
1007*0dfe541eSEvan Layton 		return (SA_OK);
1008*0dfe541eSEvan Layton 
1009*0dfe541eSEvan Layton 	err = sa_get_zfs_share_common(handle, fs_handle, NULL, zfsgroup);
1010*0dfe541eSEvan Layton 	return (err);
1011*0dfe541eSEvan Layton }
1012*0dfe541eSEvan Layton 
10138a981c33SDaniel Hoffman /*
10148a981c33SDaniel Hoffman  * Initializes only the handles specified in the sharearg for use with libshare.
10158a981c33SDaniel Hoffman  * This is used as a performance optimization relative to sa_get_zfs_shares.
10168a981c33SDaniel Hoffman  */
10178a981c33SDaniel Hoffman int
sa_get_one_zfs_share(sa_handle_t handle,char * groupname,sa_init_selective_arg_t * sharearg,char *** paths,size_t * paths_len)10188a981c33SDaniel Hoffman sa_get_one_zfs_share(sa_handle_t handle, char *groupname,
10198a981c33SDaniel Hoffman     sa_init_selective_arg_t *sharearg, char ***paths, size_t *paths_len)
10208a981c33SDaniel Hoffman {
10218a981c33SDaniel Hoffman 	sa_group_t zfsgroup;
10228a981c33SDaniel Hoffman 	libzfs_handle_t *zfs_libhandle;
10238a981c33SDaniel Hoffman 	int err;
10248a981c33SDaniel Hoffman 
10258a981c33SDaniel Hoffman 	if ((err = prep_zfs_handle_and_group(handle, groupname, &zfs_libhandle,
10268a981c33SDaniel Hoffman 	    &zfsgroup, &err)) != SA_OK) {
10278a981c33SDaniel Hoffman 		return (err);
10288a981c33SDaniel Hoffman 	}
10298a981c33SDaniel Hoffman 	/* Not an error, this could be a legacy condition */
10308a981c33SDaniel Hoffman 	if (zfsgroup == NULL)
10318a981c33SDaniel Hoffman 		return (SA_OK);
10328a981c33SDaniel Hoffman 
10338a981c33SDaniel Hoffman 	*paths_len = sharearg->zhandle_len;
1034569c0494SDaniel Hoffman 	*paths = calloc(*paths_len, sizeof (char *));
10358a981c33SDaniel Hoffman 	for (int i = 0; i < sharearg->zhandle_len; ++i) {
10368a981c33SDaniel Hoffman 		zfs_handle_t *fs_handle =
10378a981c33SDaniel Hoffman 		    ((zfs_handle_t **)(sharearg->zhandle_arr))[i];
10388a981c33SDaniel Hoffman 		if (fs_handle == NULL) {
1039569c0494SDaniel Hoffman 			/* Free non-null elements of the paths array */
1040569c0494SDaniel Hoffman 			for (int free_idx = 0; free_idx < *paths_len;
1041569c0494SDaniel Hoffman 			    ++free_idx) {
1042569c0494SDaniel Hoffman 				if ((*paths)[free_idx] != NULL)
1043569c0494SDaniel Hoffman 					free((*paths)[free_idx]);
1044569c0494SDaniel Hoffman 			}
1045569c0494SDaniel Hoffman 			free(*paths);
1046569c0494SDaniel Hoffman 			*paths = NULL;
1047569c0494SDaniel Hoffman 			*paths_len = 0;
10488a981c33SDaniel Hoffman 			return (SA_SYSTEM_ERR);
10498a981c33SDaniel Hoffman 		}
10508a981c33SDaniel Hoffman 		(*paths)[i] = malloc(sizeof (char) * ZFS_MAXPROPLEN);
10518a981c33SDaniel Hoffman 		err |= sa_get_zfs_share_common(handle, fs_handle, (*paths)[i],
10528a981c33SDaniel Hoffman 		    zfsgroup);
10538a981c33SDaniel Hoffman 	}
1054569c0494SDaniel Hoffman 
10558a981c33SDaniel Hoffman 	return (err);
10568a981c33SDaniel Hoffman }
10578a981c33SDaniel Hoffman 
10588a981c33SDaniel Hoffman /*
10598a981c33SDaniel Hoffman  * Initializes only the share with the specified sharename for use with
10608a981c33SDaniel Hoffman  * libshare.
10618a981c33SDaniel Hoffman  */
10628a981c33SDaniel Hoffman int
sa_get_zfs_share_for_name(sa_handle_t handle,char * groupname,const char * sharename,char * outpath)10638a981c33SDaniel Hoffman sa_get_zfs_share_for_name(sa_handle_t handle, char *groupname,
10648a981c33SDaniel Hoffman     const char *sharename, char *outpath)
10658a981c33SDaniel Hoffman {
10668a981c33SDaniel Hoffman 	sa_group_t zfsgroup;
10678a981c33SDaniel Hoffman 	libzfs_handle_t *zfs_libhandle;
10688a981c33SDaniel Hoffman 	int err;
10698a981c33SDaniel Hoffman 
10708a981c33SDaniel Hoffman 	if ((err = prep_zfs_handle_and_group(handle, groupname, &zfs_libhandle,
10718a981c33SDaniel Hoffman 	    &zfsgroup, &err)) != SA_OK) {
10728a981c33SDaniel Hoffman 		return (err);
10738a981c33SDaniel Hoffman 	}
10748a981c33SDaniel Hoffman 	/* Not an error, this could be a legacy condition */
10758a981c33SDaniel Hoffman 	if (zfsgroup == NULL)
10768a981c33SDaniel Hoffman 		return (SA_OK);
10778a981c33SDaniel Hoffman 
10788a981c33SDaniel Hoffman 	zfs_handle_t *fs_handle = zfs_open(zfs_libhandle,
10798a981c33SDaniel Hoffman 	    sharename + strspn(sharename, "/"), ZFS_TYPE_DATASET);
10808a981c33SDaniel Hoffman 	if (fs_handle == NULL)
10818a981c33SDaniel Hoffman 		return (SA_SYSTEM_ERR);
10828a981c33SDaniel Hoffman 
10838a981c33SDaniel Hoffman 	err = sa_get_zfs_share_common(handle, fs_handle, outpath, zfsgroup);
10848a981c33SDaniel Hoffman 	zfs_close(fs_handle);
10858a981c33SDaniel Hoffman 	return (err);
10868a981c33SDaniel Hoffman }
10878a981c33SDaniel Hoffman 
10888a981c33SDaniel Hoffman 
10898a981c33SDaniel Hoffman 
10906185db85Sdougm #define	COMMAND		"/usr/sbin/zfs"
10916185db85Sdougm 
10926185db85Sdougm /*
10936185db85Sdougm  * sa_zfs_set_sharenfs(group, path, on)
10946185db85Sdougm  *
10956185db85Sdougm  * Update the "sharenfs" property on the path. If on is true, then set
10966185db85Sdougm  * to the properties on the group or "on" if no properties are
10976185db85Sdougm  * defined. Set to "off" if on is false.
10986185db85Sdougm  */
10996185db85Sdougm 
11006185db85Sdougm int
sa_zfs_set_sharenfs(sa_group_t group,char * path,int on)11016185db85Sdougm sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
11026185db85Sdougm {
11036185db85Sdougm 	int ret = SA_NOT_IMPLEMENTED;
11046185db85Sdougm 	char *command;
11056185db85Sdougm 
11066185db85Sdougm 	command = malloc(ZFS_MAXPROPLEN * 2);
11076185db85Sdougm 	if (command != NULL) {
110857b448deSdougm 		char *opts = NULL;
110957b448deSdougm 		char *dataset = NULL;
111057b448deSdougm 		FILE *pfile;
111157b448deSdougm 		sa_handle_impl_t impl_handle;
111257b448deSdougm 		/* for now, NFS is always available for "zfs" */
111357b448deSdougm 		if (on) {
111457b448deSdougm 			opts = sa_proto_legacy_format("nfs", group, 1);
111557b448deSdougm 			if (opts != NULL && strlen(opts) == 0) {
111657b448deSdougm 				free(opts);
111757b448deSdougm 				opts = strdup("on");
111857b448deSdougm 			}
11196185db85Sdougm 		}
112057b448deSdougm 
112157b448deSdougm 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
112257b448deSdougm 		assert(impl_handle != NULL);
112357b448deSdougm 		if (impl_handle != NULL)
1124a1ef5d63Smarks 			dataset = get_zfs_dataset(impl_handle, path, B_FALSE);
112557b448deSdougm 		else
11266185db85Sdougm 			ret = SA_SYSTEM_ERR;
112757b448deSdougm 
112857b448deSdougm 		if (dataset != NULL) {
112957b448deSdougm 			(void) snprintf(command, ZFS_MAXPROPLEN * 2,
113057b448deSdougm 			    "%s set sharenfs=\"%s\" %s", COMMAND,
113157b448deSdougm 			    opts != NULL ? opts : "off", dataset);
113257b448deSdougm 			pfile = popen(command, "r");
113357b448deSdougm 			if (pfile != NULL) {
113457b448deSdougm 				ret = pclose(pfile);
113557b448deSdougm 				if (ret != 0)
113657b448deSdougm 					ret = SA_SYSTEM_ERR;
113757b448deSdougm 			}
11386185db85Sdougm 		}
113957b448deSdougm 		if (opts != NULL)
114057b448deSdougm 			free(opts);
114157b448deSdougm 		if (dataset != NULL)
114257b448deSdougm 			free(dataset);
114357b448deSdougm 		free(command);
11446185db85Sdougm 	}
11456185db85Sdougm 	return (ret);
11466185db85Sdougm }
11476185db85Sdougm 
1148da6c28aaSamw /*
1149da6c28aaSamw  * add_resources(share, opt)
1150da6c28aaSamw  *
1151da6c28aaSamw  * Add resource properties to those in "opt".  Resources are prefixed
1152da6c28aaSamw  * with name=resourcename.
1153da6c28aaSamw  */
1154da6c28aaSamw static char *
add_resources(sa_share_t share,char * opt)1155da6c28aaSamw add_resources(sa_share_t share, char *opt)
1156da6c28aaSamw {
1157da6c28aaSamw 	char *newopt = NULL;
1158da6c28aaSamw 	char *propstr;
1159da6c28aaSamw 	sa_resource_t resource;
1160da6c28aaSamw 
1161da6c28aaSamw 	newopt = strdup(opt);
1162da6c28aaSamw 	if (newopt == NULL)
1163da6c28aaSamw 		return (newopt);
1164da6c28aaSamw 
1165da6c28aaSamw 	for (resource = sa_get_share_resource(share, NULL);
1166da6c28aaSamw 	    resource != NULL;
1167da6c28aaSamw 	    resource = sa_get_next_resource(resource)) {
1168da6c28aaSamw 		char *name;
1169da6c28aaSamw 		size_t size;
1170da6c28aaSamw 
1171da6c28aaSamw 		name = sa_get_resource_attr(resource, "name");
1172da6c28aaSamw 		if (name == NULL) {
1173da6c28aaSamw 			free(newopt);
1174da6c28aaSamw 			return (NULL);
1175da6c28aaSamw 		}
1176da6c28aaSamw 		size = strlen(name) + strlen(opt) + sizeof ("name=") + 1;
1177da6c28aaSamw 		newopt = calloc(1, size);
1178da6c28aaSamw 		if (newopt != NULL)
1179da6c28aaSamw 			(void) snprintf(newopt, size, "%s,name=%s", opt, name);
1180fe1c642dSBill Krier 		sa_free_attr_string(name);
1181da6c28aaSamw 		free(opt);
1182da6c28aaSamw 		opt = newopt;
1183da6c28aaSamw 		propstr = sa_proto_legacy_format("smb", resource, 0);
1184da6c28aaSamw 		if (propstr == NULL) {
1185da6c28aaSamw 			free(opt);
1186da6c28aaSamw 			return (NULL);
1187da6c28aaSamw 		}
1188da6c28aaSamw 		size = strlen(propstr) + strlen(opt) + 2;
1189da6c28aaSamw 		newopt = calloc(1, size);
1190da6c28aaSamw 		if (newopt != NULL)
1191da6c28aaSamw 			(void) snprintf(newopt, size, "%s,%s", opt, propstr);
1192da6c28aaSamw 		free(opt);
1193da6c28aaSamw 		opt = newopt;
1194da6c28aaSamw 	}
1195da6c28aaSamw 	return (opt);
1196da6c28aaSamw }
1197da6c28aaSamw 
1198da6c28aaSamw /*
1199da6c28aaSamw  * sa_zfs_set_sharesmb(group, path, on)
1200da6c28aaSamw  *
1201da6c28aaSamw  * Update the "sharesmb" property on the path. If on is true, then set
1202da6c28aaSamw  * to the properties on the group or "on" if no properties are
1203da6c28aaSamw  * defined. Set to "off" if on is false.
1204da6c28aaSamw  */
1205da6c28aaSamw 
1206da6c28aaSamw int
sa_zfs_set_sharesmb(sa_group_t group,char * path,int on)1207da6c28aaSamw sa_zfs_set_sharesmb(sa_group_t group, char *path, int on)
1208da6c28aaSamw {
1209da6c28aaSamw 	int ret = SA_NOT_IMPLEMENTED;
1210da6c28aaSamw 	char *command;
1211da6c28aaSamw 	sa_share_t share;
1212da6c28aaSamw 
1213da6c28aaSamw 	/* In case SMB not enabled */
1214da6c28aaSamw 	if (sa_get_optionset(group, "smb") == NULL)
1215da6c28aaSamw 		return (SA_NOT_SUPPORTED);
1216da6c28aaSamw 
1217da6c28aaSamw 	command = malloc(ZFS_MAXPROPLEN * 2);
1218da6c28aaSamw 	if (command != NULL) {
1219da6c28aaSamw 		char *opts = NULL;
1220da6c28aaSamw 		char *dataset = NULL;
1221da6c28aaSamw 		FILE *pfile;
1222da6c28aaSamw 		sa_handle_impl_t impl_handle;
1223da6c28aaSamw 
1224da6c28aaSamw 		if (on) {
1225da6c28aaSamw 			char *newopt;
1226da6c28aaSamw 
1227da6c28aaSamw 			share = sa_get_share(group, NULL);
1228da6c28aaSamw 			opts = sa_proto_legacy_format("smb", share, 1);
1229da6c28aaSamw 			if (opts != NULL && strlen(opts) == 0) {
1230da6c28aaSamw 				free(opts);
1231da6c28aaSamw 				opts = strdup("on");
1232da6c28aaSamw 			}
1233da6c28aaSamw 			newopt = add_resources(opts, share);
1234da6c28aaSamw 			free(opts);
1235da6c28aaSamw 			opts = newopt;
1236da6c28aaSamw 		}
1237da6c28aaSamw 
1238da6c28aaSamw 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1239da6c28aaSamw 		assert(impl_handle != NULL);
1240da6c28aaSamw 		if (impl_handle != NULL)
1241da6c28aaSamw 			dataset = get_zfs_dataset(impl_handle, path, B_FALSE);
1242da6c28aaSamw 		else
1243da6c28aaSamw 			ret = SA_SYSTEM_ERR;
1244da6c28aaSamw 
1245da6c28aaSamw 		if (dataset != NULL) {
1246da6c28aaSamw 			(void) snprintf(command, ZFS_MAXPROPLEN * 2,
1247da6c28aaSamw 			    "echo %s set sharesmb=\"%s\" %s", COMMAND,
1248da6c28aaSamw 			    opts != NULL ? opts : "off", dataset);
1249da6c28aaSamw 			pfile = popen(command, "r");
1250da6c28aaSamw 			if (pfile != NULL) {
1251da6c28aaSamw 				ret = pclose(pfile);
1252da6c28aaSamw 				if (ret != 0)
1253da6c28aaSamw 					ret = SA_SYSTEM_ERR;
1254da6c28aaSamw 			}
1255da6c28aaSamw 		}
1256da6c28aaSamw 		if (opts != NULL)
1257da6c28aaSamw 			free(opts);
1258da6c28aaSamw 		if (dataset != NULL)
1259da6c28aaSamw 			free(dataset);
1260da6c28aaSamw 		free(command);
1261da6c28aaSamw 	}
1262da6c28aaSamw 	return (ret);
1263da6c28aaSamw }
1264da6c28aaSamw 
12656185db85Sdougm /*
12666185db85Sdougm  * sa_zfs_update(group)
12676185db85Sdougm  *
12686185db85Sdougm  * call back to ZFS to update the share if necessary.
12696185db85Sdougm  * Don't do it if it isn't a real change.
12706185db85Sdougm  */
12716185db85Sdougm int
sa_zfs_update(sa_group_t group)12726185db85Sdougm sa_zfs_update(sa_group_t group)
12736185db85Sdougm {
12746185db85Sdougm 	sa_optionset_t protopt;
12756185db85Sdougm 	sa_group_t parent;
12766185db85Sdougm 	char *command;
12776185db85Sdougm 	char *optstring;
12786185db85Sdougm 	int ret = SA_OK;
12796185db85Sdougm 	int doupdate = 0;
12806185db85Sdougm 	FILE *pfile;
12816185db85Sdougm 
12826185db85Sdougm 	if (sa_is_share(group))
128357b448deSdougm 		parent = sa_get_parent_group(group);
12846185db85Sdougm 	else
128557b448deSdougm 		parent = group;
12866185db85Sdougm 
12876185db85Sdougm 	if (parent != NULL) {
128857b448deSdougm 		command = malloc(ZFS_MAXPROPLEN * 2);
128957b448deSdougm 		if (command == NULL)
129057b448deSdougm 			return (SA_NO_MEMORY);
129157b448deSdougm 
129257b448deSdougm 		*command = '\0';
129357b448deSdougm 		for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
129457b448deSdougm 		    protopt = sa_get_next_optionset(protopt)) {
129557b448deSdougm 
129657b448deSdougm 			char *proto = sa_get_optionset_attr(protopt, "type");
129757b448deSdougm 			char *path;
129857b448deSdougm 			char *dataset = NULL;
129957b448deSdougm 			char *zfsopts = NULL;
130057b448deSdougm 
130157b448deSdougm 			if (sa_is_share(group)) {
130257b448deSdougm 				path = sa_get_share_attr((sa_share_t)group,
130357b448deSdougm 				    "path");
130457b448deSdougm 				if (path != NULL) {
130557b448deSdougm 					sa_handle_impl_t impl_handle;
130657b448deSdougm 
130757b448deSdougm 					impl_handle = sa_find_group_handle(
130857b448deSdougm 					    group);
130957b448deSdougm 					if (impl_handle != NULL)
131057b448deSdougm 						dataset = get_zfs_dataset(
1311a1ef5d63Smarks 						    impl_handle, path, B_FALSE);
131257b448deSdougm 					else
131357b448deSdougm 						ret = SA_SYSTEM_ERR;
131457b448deSdougm 
131557b448deSdougm 					sa_free_attr_string(path);
131657b448deSdougm 				}
13176185db85Sdougm 			} else {
131857b448deSdougm 				dataset = sa_get_group_attr(group, "name");
13196185db85Sdougm 			}
132057b448deSdougm 			/* update only when there is an optstring found */
132157b448deSdougm 			doupdate = 0;
132257b448deSdougm 			if (proto != NULL && dataset != NULL) {
132357b448deSdougm 				optstring = sa_proto_legacy_format(proto,
132457b448deSdougm 				    group, 1);
132557b448deSdougm 				zfsopts = get_zfs_property(dataset,
132657b448deSdougm 				    ZFS_PROP_SHARENFS);
132757b448deSdougm 
132857b448deSdougm 				if (optstring != NULL && zfsopts != NULL) {
132957b448deSdougm 					if (strcmp(optstring, zfsopts) != 0)
133057b448deSdougm 						doupdate++;
133157b448deSdougm 				}
133257b448deSdougm 				if (doupdate) {
133357b448deSdougm 					if (optstring != NULL &&
133457b448deSdougm 					    strlen(optstring) > 0) {
133557b448deSdougm 						(void) snprintf(command,
133657b448deSdougm 						    ZFS_MAXPROPLEN * 2,
13371f713840SDoug McCallum 						    "%s set share%s=%s %s",
13381f713840SDoug McCallum 						    COMMAND, proto,
133957b448deSdougm 						    optstring, dataset);
134057b448deSdougm 					} else {
134157b448deSdougm 						(void) snprintf(command,
134257b448deSdougm 						    ZFS_MAXPROPLEN * 2,
13431f713840SDoug McCallum 						    "%s set share%s=on %s",
13441f713840SDoug McCallum 						    COMMAND, proto,
134557b448deSdougm 						    dataset);
134657b448deSdougm 					}
134757b448deSdougm 					pfile = popen(command, "r");
134857b448deSdougm 					if (pfile != NULL)
134957b448deSdougm 						ret = pclose(pfile);
135057b448deSdougm 					switch (ret) {
135157b448deSdougm 					default:
135257b448deSdougm 					case 1:
135357b448deSdougm 						ret = SA_SYSTEM_ERR;
135457b448deSdougm 						break;
135557b448deSdougm 					case 2:
135657b448deSdougm 						ret = SA_SYNTAX_ERR;
135757b448deSdougm 						break;
135857b448deSdougm 					case 0:
135957b448deSdougm 						break;
136057b448deSdougm 					}
136157b448deSdougm 				}
136257b448deSdougm 				if (optstring != NULL)
136357b448deSdougm 					free(optstring);
136457b448deSdougm 				if (zfsopts != NULL)
136557b448deSdougm 					free(zfsopts);
13666185db85Sdougm 			}
136757b448deSdougm 			if (proto != NULL)
136857b448deSdougm 				sa_free_attr_string(proto);
136957b448deSdougm 			if (dataset != NULL)
137057b448deSdougm 				free(dataset);
13716185db85Sdougm 		}
137257b448deSdougm 		free(command);
13736185db85Sdougm 	}
13746185db85Sdougm 	return (ret);
13756185db85Sdougm }
13766185db85Sdougm 
13776185db85Sdougm /*
13786185db85Sdougm  * sa_group_is_zfs(group)
13796185db85Sdougm  *
13806185db85Sdougm  * Given the group, determine if the zfs attribute is set.
13816185db85Sdougm  */
13826185db85Sdougm 
13836185db85Sdougm int
sa_group_is_zfs(sa_group_t group)13846185db85Sdougm sa_group_is_zfs(sa_group_t group)
13856185db85Sdougm {
13866185db85Sdougm 	char *zfs;
13876185db85Sdougm 	int ret = 0;
13886185db85Sdougm 
13896185db85Sdougm 	zfs = sa_get_group_attr(group, "zfs");
13906185db85Sdougm 	if (zfs != NULL) {
139157b448deSdougm 		ret = 1;
139257b448deSdougm 		sa_free_attr_string(zfs);
13936185db85Sdougm 	}
13946185db85Sdougm 	return (ret);
13956185db85Sdougm }
13966185db85Sdougm 
13976185db85Sdougm /*
13986185db85Sdougm  * sa_path_is_zfs(path)
13996185db85Sdougm  *
14006185db85Sdougm  * Check to see if the file system path represents is of type "zfs".
14016185db85Sdougm  */
14026185db85Sdougm 
14036185db85Sdougm int
sa_path_is_zfs(char * path)14046185db85Sdougm sa_path_is_zfs(char *path)
14056185db85Sdougm {
14066185db85Sdougm 	char *fstype;
14076185db85Sdougm 	int ret = 0;
14086185db85Sdougm 
14096185db85Sdougm 	fstype = sa_fstype(path);
141057b448deSdougm 	if (fstype != NULL && strcmp(fstype, "zfs") == 0)
141157b448deSdougm 		ret = 1;
14126185db85Sdougm 	if (fstype != NULL)
141357b448deSdougm 		sa_free_fstype(fstype);
14146185db85Sdougm 	return (ret);
14156185db85Sdougm }
1416ecd6cf80Smarks 
1417ecd6cf80Smarks int
sa_sharetab_fill_zfs(sa_share_t share,share_t * sh,char * proto)1418ecd6cf80Smarks sa_sharetab_fill_zfs(sa_share_t share, share_t *sh, char *proto)
1419ecd6cf80Smarks {
1420ecd6cf80Smarks 	char *path;
1421ecd6cf80Smarks 
1422ed78bdc4Smarks 	/* Make sure path is valid */
1423ed78bdc4Smarks 
1424ecd6cf80Smarks 	path = sa_get_share_attr(share, "path");
1425ecd6cf80Smarks 	if (path != NULL) {
1426ecd6cf80Smarks 		(void) memset(sh, 0, sizeof (sh));
1427ecd6cf80Smarks 		(void) sa_fillshare(share, proto, sh);
1428ed78bdc4Smarks 		sa_free_attr_string(path);
1429ecd6cf80Smarks 		return (0);
1430ecd6cf80Smarks 	} else
1431ecd6cf80Smarks 		return (1);
1432ecd6cf80Smarks }
1433ecd6cf80Smarks 
1434ecd6cf80Smarks #define	SMAX(i, j)	\
1435ecd6cf80Smarks 	if ((j) > (i)) { \
1436ecd6cf80Smarks 		(i) = (j); \
1437ecd6cf80Smarks 	}
1438ecd6cf80Smarks 
1439ecd6cf80Smarks int
sa_share_zfs(sa_share_t share,sa_resource_t resource,char * path,share_t * sh,void * exportdata,zfs_share_op_t operation)1440743a77edSAlan Wright sa_share_zfs(sa_share_t share, sa_resource_t resource, char *path, share_t *sh,
1441da6c28aaSamw     void *exportdata, zfs_share_op_t operation)
1442ecd6cf80Smarks {
1443ecd6cf80Smarks 	libzfs_handle_t *libhandle;
1444ecd6cf80Smarks 	sa_group_t group;
1445ecd6cf80Smarks 	sa_handle_t sahandle;
1446ecd6cf80Smarks 	char *dataset;
1447ecd6cf80Smarks 	int err = EINVAL;
1448ecd6cf80Smarks 	int i, j;
14494d79fe3bSmarks 	char newpath[MAXPATHLEN];
1450a1ef5d63Smarks 	char *pathp;
1451ecd6cf80Smarks 
1452ecd6cf80Smarks 	/*
1453ecd6cf80Smarks 	 * First find the dataset name
1454ecd6cf80Smarks 	 */
1455ecd6cf80Smarks 	if ((group = sa_get_parent_group(share)) == NULL)  {
1456a63214d6SBill Krier 		return (EINVAL);
1457ecd6cf80Smarks 	}
1458ecd6cf80Smarks 	if ((sahandle = sa_find_group_handle(group)) == NULL) {
1459a63214d6SBill Krier 		return (EINVAL);
1460ecd6cf80Smarks 	}
1461ecd6cf80Smarks 
14624d79fe3bSmarks 	/*
14634d79fe3bSmarks 	 * If get_zfs_dataset fails, see if it is a subdirectory
14644d79fe3bSmarks 	 */
1465a1ef5d63Smarks 
1466a1ef5d63Smarks 	pathp = path;
1467a1ef5d63Smarks 	while ((dataset = get_zfs_dataset(sahandle, pathp, B_TRUE)) == NULL) {
14684d79fe3bSmarks 		char *p;
14694d79fe3bSmarks 
1470a1ef5d63Smarks 		if (pathp == path) {
1471a1ef5d63Smarks 			(void) strlcpy(newpath, path, sizeof (newpath));
1472a1ef5d63Smarks 			pathp = newpath;
1473a1ef5d63Smarks 		}
1474a1ef5d63Smarks 
1475d34e4517SDoug McCallum 		/*
1476d34e4517SDoug McCallum 		 * Make sure only one leading '/' This condition came
1477d34e4517SDoug McCallum 		 * about when using HAStoragePlus which insisted on
1478d34e4517SDoug McCallum 		 * putting an extra leading '/' in the ZFS path
1479d34e4517SDoug McCallum 		 * name. The problem is fixed in other areas, but this
1480d34e4517SDoug McCallum 		 * will catch any other ways that a double slash might
1481d34e4517SDoug McCallum 		 * get introduced.
1482d34e4517SDoug McCallum 		 */
1483d34e4517SDoug McCallum 		while (*pathp == '/' && *(pathp + 1) == '/')
1484d34e4517SDoug McCallum 			pathp++;
1485d34e4517SDoug McCallum 
1486a1ef5d63Smarks 		/*
1487a1ef5d63Smarks 		 * chop off part of path, but if we are at root then
1488a1ef5d63Smarks 		 * make sure path is a /
1489a1ef5d63Smarks 		 */
1490a1ef5d63Smarks 		if ((strlen(pathp) > 1) && (p = strrchr(pathp, '/'))) {
1491a1ef5d63Smarks 			if (pathp == p) {
1492a1ef5d63Smarks 				*(p + 1) = '\0';  /* skip over /, root case */
1493a1ef5d63Smarks 			} else {
1494a1ef5d63Smarks 				*p = '\0';
1495a1ef5d63Smarks 			}
1496a1ef5d63Smarks 		} else {
1497a63214d6SBill Krier 			return (EINVAL);
1498a1ef5d63Smarks 		}
1499ecd6cf80Smarks 	}
1500ecd6cf80Smarks 
1501ecd6cf80Smarks 	libhandle = libzfs_init();
1502ecd6cf80Smarks 	if (libhandle != NULL) {
1503743a77edSAlan Wright 		char *resource_name;
1504ecd6cf80Smarks 
1505ecd6cf80Smarks 		i = (sh->sh_path ? strlen(sh->sh_path) : 0);
1506ecd6cf80Smarks 		sh->sh_size = i;
1507ecd6cf80Smarks 
1508ecd6cf80Smarks 		j = (sh->sh_res ? strlen(sh->sh_res) : 0);
1509ecd6cf80Smarks 		sh->sh_size += j;
1510ecd6cf80Smarks 		SMAX(i, j);
1511ecd6cf80Smarks 
1512ecd6cf80Smarks 		j = (sh->sh_fstype ? strlen(sh->sh_fstype) : 0);
1513ecd6cf80Smarks 		sh->sh_size += j;
1514ecd6cf80Smarks 		SMAX(i, j);
1515ecd6cf80Smarks 
1516ecd6cf80Smarks 		j = (sh->sh_opts ? strlen(sh->sh_opts) : 0);
1517ecd6cf80Smarks 		sh->sh_size += j;
1518ecd6cf80Smarks 		SMAX(i, j);
1519ecd6cf80Smarks 
1520ecd6cf80Smarks 		j = (sh->sh_descr ? strlen(sh->sh_descr) : 0);
1521ecd6cf80Smarks 		sh->sh_size += j;
1522ecd6cf80Smarks 		SMAX(i, j);
1523743a77edSAlan Wright 
1524743a77edSAlan Wright 		resource_name = sa_get_resource_attr(resource, "name");
1525743a77edSAlan Wright 
1526ecd6cf80Smarks 		err = zfs_deleg_share_nfs(libhandle, dataset, path,
1527743a77edSAlan Wright 		    resource_name, exportdata, sh, i, operation);
15285b6e0c46Sdougm 		if (err == SA_OK)
15295b6e0c46Sdougm 			sa_update_sharetab_ts(sahandle);
1530a63214d6SBill Krier 		else
1531a63214d6SBill Krier 			err = errno;
1532743a77edSAlan Wright 		if (resource_name)
1533743a77edSAlan Wright 			sa_free_attr_string(resource_name);
1534743a77edSAlan Wright 
1535ecd6cf80Smarks 		libzfs_fini(libhandle);
1536ecd6cf80Smarks 	}
1537ecd6cf80Smarks 	free(dataset);
1538ecd6cf80Smarks 	return (err);
1539ecd6cf80Smarks }
15405b6e0c46Sdougm 
15415b6e0c46Sdougm /*
15425b6e0c46Sdougm  * sa_get_zfs_handle(handle)
15435b6e0c46Sdougm  *
15445b6e0c46Sdougm  * Given an sa_handle_t, return the libzfs_handle_t *. This is only
15455b6e0c46Sdougm  * used internally by libzfs. Needed in order to avoid including
15465b6e0c46Sdougm  * libshare_impl.h in libzfs.
15475b6e0c46Sdougm  */
15485b6e0c46Sdougm 
15495b6e0c46Sdougm libzfs_handle_t *
sa_get_zfs_handle(sa_handle_t handle)15505b6e0c46Sdougm sa_get_zfs_handle(sa_handle_t handle)
15515b6e0c46Sdougm {
15525b6e0c46Sdougm 	sa_handle_impl_t implhandle = (sa_handle_impl_t)handle;
15535b6e0c46Sdougm 
15545b6e0c46Sdougm 	return (implhandle->zfs_libhandle);
15555b6e0c46Sdougm }
1556743a77edSAlan Wright 
1557743a77edSAlan Wright /*
1558743a77edSAlan Wright  * sa_get_zfs_info(libzfs, path, mountpoint, dataset)
1559743a77edSAlan Wright  *
1560743a77edSAlan Wright  * Find the ZFS dataset and mountpoint for a given path
1561743a77edSAlan Wright  */
1562743a77edSAlan Wright int
sa_zfs_get_info(libzfs_handle_t * libzfs,char * path,char * mountpointp,char * datasetp)1563743a77edSAlan Wright sa_zfs_get_info(libzfs_handle_t *libzfs, char *path, char *mountpointp,
1564743a77edSAlan Wright     char *datasetp)
1565743a77edSAlan Wright {
1566743a77edSAlan Wright 	get_all_cbdata_t cb = { 0 };
1567743a77edSAlan Wright 	int i;
1568743a77edSAlan Wright 	char mountpoint[ZFS_MAXPROPLEN];
1569743a77edSAlan Wright 	char dataset[ZFS_MAXPROPLEN];
1570743a77edSAlan Wright 	char canmount[ZFS_MAXPROPLEN];
1571743a77edSAlan Wright 	char *dp;
1572743a77edSAlan Wright 	int count;
1573743a77edSAlan Wright 	int ret = 0;
1574743a77edSAlan Wright 
1575743a77edSAlan Wright 	cb.cb_types = ZFS_TYPE_FILESYSTEM;
1576743a77edSAlan Wright 
1577743a77edSAlan Wright 	if (libzfs == NULL)
1578743a77edSAlan Wright 		return (0);
1579743a77edSAlan Wright 
1580743a77edSAlan Wright 	(void) zfs_iter_root(libzfs, get_one_filesystem, &cb);
1581743a77edSAlan Wright 	count = cb.cb_used;
1582743a77edSAlan Wright 
1583743a77edSAlan Wright 	qsort(cb.cb_handles, count, sizeof (void *), mountpoint_compare);
1584743a77edSAlan Wright 	for (i = 0; i < count; i++) {
1585743a77edSAlan Wright 		/* must have a mountpoint */
1586743a77edSAlan Wright 		if (zfs_prop_get(cb.cb_handles[i], ZFS_PROP_MOUNTPOINT,
1587743a77edSAlan Wright 		    mountpoint, sizeof (mountpoint),
1588743a77edSAlan Wright 		    NULL, NULL, 0, B_FALSE) != 0) {
1589743a77edSAlan Wright 			/* no mountpoint */
1590743a77edSAlan Wright 			continue;
1591743a77edSAlan Wright 		}
1592743a77edSAlan Wright 
1593743a77edSAlan Wright 		/* mountpoint must be a path */
1594743a77edSAlan Wright 		if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
1595743a77edSAlan Wright 		    strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1596743a77edSAlan Wright 			/*
1597743a77edSAlan Wright 			 * Search mmttab for mountpoint
1598743a77edSAlan Wright 			 */
1599743a77edSAlan Wright 
1600743a77edSAlan Wright 			if (get_legacy_mountpoint(path, dataset,
1601743a77edSAlan Wright 			    ZFS_MAXPROPLEN, mountpoint,
1602743a77edSAlan Wright 			    ZFS_MAXPROPLEN) == 0) {
1603743a77edSAlan Wright 				ret = 1;
1604743a77edSAlan Wright 				break;
1605743a77edSAlan Wright 			}
1606743a77edSAlan Wright 			continue;
1607743a77edSAlan Wright 		}
1608743a77edSAlan Wright 
1609743a77edSAlan Wright 		/* canmount must be set */
1610743a77edSAlan Wright 		canmount[0] = '\0';
1611743a77edSAlan Wright 		if (zfs_prop_get(cb.cb_handles[i], ZFS_PROP_CANMOUNT, canmount,
1612743a77edSAlan Wright 		    sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 ||
1613743a77edSAlan Wright 		    strcmp(canmount, "off") == 0)
1614743a77edSAlan Wright 			continue;
1615743a77edSAlan Wright 
1616743a77edSAlan Wright 		/*
1617743a77edSAlan Wright 		 * have a mountable handle but want to skip those marked none
1618743a77edSAlan Wright 		 * and legacy
1619743a77edSAlan Wright 		 */
1620743a77edSAlan Wright 		if (strcmp(mountpoint, path) == 0) {
1621743a77edSAlan Wright 			dp = (char *)zfs_get_name(cb.cb_handles[i]);
1622743a77edSAlan Wright 			if (dp != NULL) {
1623743a77edSAlan Wright 				if (datasetp != NULL)
1624743a77edSAlan Wright 					(void) strcpy(datasetp, dp);
1625743a77edSAlan Wright 				if (mountpointp != NULL)
1626743a77edSAlan Wright 					(void) strcpy(mountpointp, mountpoint);
1627743a77edSAlan Wright 				ret = 1;
1628743a77edSAlan Wright 			}
1629743a77edSAlan Wright 			break;
1630743a77edSAlan Wright 		}
1631743a77edSAlan Wright 
1632743a77edSAlan Wright 	}
1633743a77edSAlan Wright 
1634743a77edSAlan Wright 	return (ret);
1635743a77edSAlan Wright }
1636148c5f43SAlan Wright 
1637148c5f43SAlan Wright /*
1638148c5f43SAlan Wright  * This method builds values for "sharesmb" property from the
1639148c5f43SAlan Wright  * nvlist argument. The values are returned in sharesmb_val variable.
1640148c5f43SAlan Wright  */
1641148c5f43SAlan Wright static int
sa_zfs_sprintf_new_prop(nvlist_t * nvl,char * sharesmb_val)1642148c5f43SAlan Wright sa_zfs_sprintf_new_prop(nvlist_t *nvl, char *sharesmb_val)
1643148c5f43SAlan Wright {
1644148c5f43SAlan Wright 	char cur_val[MAXPATHLEN];
1645148c5f43SAlan Wright 	char *name, *val;
1646148c5f43SAlan Wright 	nvpair_t *cur;
1647148c5f43SAlan Wright 	int err = 0;
1648148c5f43SAlan Wright 
1649148c5f43SAlan Wright 	cur = nvlist_next_nvpair(nvl, NULL);
1650148c5f43SAlan Wright 	while (cur != NULL) {
1651148c5f43SAlan Wright 		name = nvpair_name(cur);
1652148c5f43SAlan Wright 		err = nvpair_value_string(cur, &val);
1653148c5f43SAlan Wright 		if ((err != 0) || (name == NULL) || (val == NULL))
1654148c5f43SAlan Wright 			return (-1);
1655148c5f43SAlan Wright 
1656148c5f43SAlan Wright 		(void) snprintf(cur_val, MAXPATHLEN, "%s=%s,", name, val);
1657148c5f43SAlan Wright 		(void) strlcat(sharesmb_val, cur_val, MAXPATHLEN);
1658148c5f43SAlan Wright 
1659148c5f43SAlan Wright 		cur = nvlist_next_nvpair(nvl, cur);
1660148c5f43SAlan Wright 	}
1661148c5f43SAlan Wright 
1662148c5f43SAlan Wright 	return (0);
1663148c5f43SAlan Wright }
1664148c5f43SAlan Wright 
1665148c5f43SAlan Wright /*
1666148c5f43SAlan Wright  * This method builds values for "sharesmb" property from values
1667148c5f43SAlan Wright  * already existing on the share. The properties set via sa_zfs_sprint_new_prop
1668148c5f43SAlan Wright  * method are passed in sharesmb_val. If a existing property is already
1669148c5f43SAlan Wright  * set via sa_zfs_sprint_new_prop method, then they are not appended
1670148c5f43SAlan Wright  * to the sharesmb_val string. The returned sharesmb_val string is a combination
1671148c5f43SAlan Wright  * of new and existing values for 'sharesmb' property.
1672148c5f43SAlan Wright  */
1673148c5f43SAlan Wright static int
sa_zfs_sprintf_existing_prop(zfs_handle_t * handle,char * sharesmb_val)1674148c5f43SAlan Wright sa_zfs_sprintf_existing_prop(zfs_handle_t *handle, char *sharesmb_val)
1675148c5f43SAlan Wright {
1676148c5f43SAlan Wright 	char shareopts[ZFS_MAXPROPLEN], cur_val[MAXPATHLEN];
1677148c5f43SAlan Wright 	char *token, *last, *value;
1678148c5f43SAlan Wright 
1679148c5f43SAlan Wright 	if (zfs_prop_get(handle, ZFS_PROP_SHARESMB, shareopts,
1680148c5f43SAlan Wright 	    sizeof (shareopts), NULL, NULL, 0, B_FALSE) != 0)
1681148c5f43SAlan Wright 		return (-1);
1682148c5f43SAlan Wright 
1683148c5f43SAlan Wright 	if (strstr(shareopts, "=") == NULL)
1684148c5f43SAlan Wright 		return (0);
1685148c5f43SAlan Wright 
1686148c5f43SAlan Wright 	for (token = strtok_r(shareopts, ",", &last); token != NULL;
1687148c5f43SAlan Wright 	    token = strtok_r(NULL, ",", &last)) {
1688148c5f43SAlan Wright 		value = strchr(token, '=');
1689148c5f43SAlan Wright 		if (value == NULL)
1690148c5f43SAlan Wright 			return (-1);
1691148c5f43SAlan Wright 		*value++ = '\0';
1692148c5f43SAlan Wright 
1693148c5f43SAlan Wright 		(void) snprintf(cur_val, MAXPATHLEN, "%s=", token);
1694148c5f43SAlan Wright 		if (strstr(sharesmb_val, cur_val) == NULL) {
1695148c5f43SAlan Wright 			(void) strlcat(cur_val, value, MAXPATHLEN);
1696148c5f43SAlan Wright 			(void) strlcat(cur_val, ",", MAXPATHLEN);
1697148c5f43SAlan Wright 			(void) strlcat(sharesmb_val, cur_val, MAXPATHLEN);
1698148c5f43SAlan Wright 		}
1699148c5f43SAlan Wright 	}
1700148c5f43SAlan Wright 
1701148c5f43SAlan Wright 	return (0);
1702148c5f43SAlan Wright }
1703148c5f43SAlan Wright 
1704148c5f43SAlan Wright /*
1705148c5f43SAlan Wright  * Sets the share properties on a ZFS share. For now, this method sets only
1706148c5f43SAlan Wright  * the "sharesmb" property.
1707148c5f43SAlan Wright  *
1708148c5f43SAlan Wright  * This method includes building a comma seperated name-value string to be
1709148c5f43SAlan Wright  * set on the "sharesmb" property of a ZFS share. This name-value string is
1710148c5f43SAlan Wright  * build in 2 steps:
1711148c5f43SAlan Wright  *    - New property values given as name-value pair are set first.
1712148c5f43SAlan Wright  *    - Existing optionset properties, which are not part of the new properties
1713148c5f43SAlan Wright  *	passed in step 1, are appended to the newly set properties.
1714148c5f43SAlan Wright  */
1715148c5f43SAlan Wright int
sa_zfs_setprop(sa_handle_t handle,char * path,nvlist_t * nvl)1716148c5f43SAlan Wright sa_zfs_setprop(sa_handle_t handle, char *path, nvlist_t *nvl)
1717148c5f43SAlan Wright {
1718148c5f43SAlan Wright 	zfs_handle_t *z_fs;
1719148c5f43SAlan Wright 	libzfs_handle_t *z_lib;
1720148c5f43SAlan Wright 	char sharesmb_val[MAXPATHLEN];
1721148c5f43SAlan Wright 	char *dataset, *lastcomma;
1722148c5f43SAlan Wright 
1723148c5f43SAlan Wright 	if (nvlist_empty(nvl))
1724148c5f43SAlan Wright 		return (0);
1725148c5f43SAlan Wright 
1726148c5f43SAlan Wright 	if ((handle == NULL) || (path == NULL))
1727148c5f43SAlan Wright 		return (-1);
1728148c5f43SAlan Wright 
1729148c5f43SAlan Wright 	if ((dataset = get_zfs_dataset(handle, path, B_FALSE)) == NULL)
1730148c5f43SAlan Wright 		return (-1);
1731148c5f43SAlan Wright 
1732148c5f43SAlan Wright 	if ((z_lib = libzfs_init()) == NULL) {
1733148c5f43SAlan Wright 		free(dataset);
1734148c5f43SAlan Wright 		return (-1);
1735148c5f43SAlan Wright 	}
1736148c5f43SAlan Wright 
1737148c5f43SAlan Wright 	z_fs = zfs_open(z_lib, dataset, ZFS_TYPE_DATASET);
1738148c5f43SAlan Wright 	if (z_fs == NULL) {
1739148c5f43SAlan Wright 		free(dataset);
1740148c5f43SAlan Wright 		libzfs_fini(z_lib);
1741148c5f43SAlan Wright 		return (-1);
1742148c5f43SAlan Wright 	}
1743148c5f43SAlan Wright 
1744148c5f43SAlan Wright 	bzero(sharesmb_val, MAXPATHLEN);
1745148c5f43SAlan Wright 	if (sa_zfs_sprintf_new_prop(nvl, sharesmb_val) != 0) {
1746148c5f43SAlan Wright 		free(dataset);
1747148c5f43SAlan Wright 		zfs_close(z_fs);
1748148c5f43SAlan Wright 		libzfs_fini(z_lib);
1749148c5f43SAlan Wright 		return (-1);
1750148c5f43SAlan Wright 	}
1751148c5f43SAlan Wright 
1752148c5f43SAlan Wright 	if (sa_zfs_sprintf_existing_prop(z_fs, sharesmb_val) != 0) {
1753148c5f43SAlan Wright 		free(dataset);
1754148c5f43SAlan Wright 		zfs_close(z_fs);
1755148c5f43SAlan Wright 		libzfs_fini(z_lib);
1756148c5f43SAlan Wright 		return (-1);
1757148c5f43SAlan Wright 	}
1758148c5f43SAlan Wright 
1759148c5f43SAlan Wright 	lastcomma = strrchr(sharesmb_val, ',');
1760148c5f43SAlan Wright 	if ((lastcomma != NULL) && (lastcomma[1] == '\0'))
1761148c5f43SAlan Wright 		*lastcomma = '\0';
1762148c5f43SAlan Wright 
1763148c5f43SAlan Wright 	(void) zfs_prop_set(z_fs, zfs_prop_to_name(ZFS_PROP_SHARESMB),
1764148c5f43SAlan Wright 	    sharesmb_val);
1765148c5f43SAlan Wright 	free(dataset);
1766148c5f43SAlan Wright 	zfs_close(z_fs);
1767148c5f43SAlan Wright 	libzfs_fini(z_lib);
1768148c5f43SAlan Wright 
1769148c5f43SAlan Wright 	return (0);
1770148c5f43SAlan Wright }
1771