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 /*
2324424a35Sdougm  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
246185db85Sdougm  * Use is subject to license terms.
256185db85Sdougm  */
266185db85Sdougm 
276185db85Sdougm #pragma ident	"%Z%%M%	%I%	%E% SMI"
286185db85Sdougm 
296185db85Sdougm #include <libzfs.h>
306185db85Sdougm #include <string.h>
31*a3351425Sdougm #include <strings.h>
326185db85Sdougm #include <libshare.h>
336185db85Sdougm #include "libshare_impl.h"
341cea05afSdougm #include <libintl.h>
356185db85Sdougm 
366185db85Sdougm extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *);
376185db85Sdougm extern sa_group_t _sa_create_zfs_group(sa_group_t, char *);
386185db85Sdougm extern char *sa_fstype(char *);
396185db85Sdougm extern void set_node_attr(void *, char *, char *);
406185db85Sdougm extern int sa_is_share(void *);
411cea05afSdougm 
421cea05afSdougm /*
431cea05afSdougm  * File system specific code for ZFS. The original code was stolen
441cea05afSdougm  * from the "zfs" command and modified to better suit this library's
451cea05afSdougm  * usage.
461cea05afSdougm  */
471cea05afSdougm 
481cea05afSdougm typedef struct get_all_cbdata {
491cea05afSdougm 	zfs_handle_t	**cb_handles;
501cea05afSdougm 	size_t		cb_alloc;
511cea05afSdougm 	size_t		cb_used;
52*a3351425Sdougm 	uint_t		cb_types;
531cea05afSdougm } get_all_cbdata_t;
541cea05afSdougm 
551cea05afSdougm /*
56549ec3ffSdougm  * sa_zfs_init(impl_handle)
571cea05afSdougm  *
58549ec3ffSdougm  * Initialize an access handle into libzfs.  The handle needs to stay
59549ec3ffSdougm  * around until sa_zfs_fini() in order to maintain the cache of
60549ec3ffSdougm  * mounts.
611cea05afSdougm  */
621cea05afSdougm 
6357b448deSdougm int
64549ec3ffSdougm sa_zfs_init(sa_handle_impl_t impl_handle)
651cea05afSdougm {
66549ec3ffSdougm 	impl_handle->zfs_libhandle = libzfs_init();
6757b448deSdougm 	if (impl_handle->zfs_libhandle != NULL) {
6857b448deSdougm 		libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE);
6957b448deSdougm 		return (B_TRUE);
7057b448deSdougm 	}
7157b448deSdougm 	return (B_FALSE);
721cea05afSdougm }
731cea05afSdougm 
741cea05afSdougm /*
75549ec3ffSdougm  * sa_zfs_fini(impl_handle)
761cea05afSdougm  *
771cea05afSdougm  * cleanup data structures and the libzfs handle used for accessing
781cea05afSdougm  * zfs file share info.
791cea05afSdougm  */
801cea05afSdougm 
811cea05afSdougm void
82549ec3ffSdougm sa_zfs_fini(sa_handle_impl_t impl_handle)
831cea05afSdougm {
84549ec3ffSdougm 	if (impl_handle->zfs_libhandle != NULL) {
8557b448deSdougm 		if (impl_handle->zfs_list != NULL) {
86*a3351425Sdougm 			zfs_handle_t **zhp = impl_handle->zfs_list;
87*a3351425Sdougm 			size_t i;
88*a3351425Sdougm 
8957b448deSdougm 			/*
90*a3351425Sdougm 			 * Contents of zfs_list need to be freed so we
91*a3351425Sdougm 			 * don't lose ZFS handles.
9257b448deSdougm 			 */
93*a3351425Sdougm 			for (i = 0; i < impl_handle->zfs_list_count; i++) {
94*a3351425Sdougm 				zfs_close(zhp[i]);
95*a3351425Sdougm 			}
9657b448deSdougm 			free(impl_handle->zfs_list);
9757b448deSdougm 			impl_handle->zfs_list = NULL;
9857b448deSdougm 			impl_handle->zfs_list_count = 0;
9957b448deSdougm 		}
100*a3351425Sdougm 
101*a3351425Sdougm 		libzfs_fini(impl_handle->zfs_libhandle);
102*a3351425Sdougm 		impl_handle->zfs_libhandle = NULL;
1031cea05afSdougm 	}
1041cea05afSdougm }
1051cea05afSdougm 
1061cea05afSdougm /*
1071cea05afSdougm  * get_one_filesystem(zfs_handle_t, data)
1081cea05afSdougm  *
1091cea05afSdougm  * an interator function called while iterating through the ZFS
1101cea05afSdougm  * root. It accumulates into an array of file system handles that can
1111cea05afSdougm  * be used to derive info about those file systems.
112*a3351425Sdougm  *
113*a3351425Sdougm  * Note that as this function is called, we close all zhp handles that
114*a3351425Sdougm  * are not going to be places into the cp_handles list. We don't want
115*a3351425Sdougm  * to close the ones we are keeping, but all others would be leaked if
116*a3351425Sdougm  * not closed here.
1171cea05afSdougm  */
1181cea05afSdougm 
1191cea05afSdougm static int
1201cea05afSdougm get_one_filesystem(zfs_handle_t *zhp, void *data)
1211cea05afSdougm {
1221cea05afSdougm 	get_all_cbdata_t *cbp = data;
123*a3351425Sdougm 	zfs_type_t type = zfs_get_type(zhp);
1241cea05afSdougm 
1251cea05afSdougm 	/*
126*a3351425Sdougm 	 * Interate over any nested datasets.
1271cea05afSdougm 	 */
128*a3351425Sdougm 	if (type == ZFS_TYPE_FILESYSTEM &&
129*a3351425Sdougm 	    zfs_iter_filesystems(zhp, get_one_filesystem, data) != 0) {
130*a3351425Sdougm 		zfs_close(zhp);
131*a3351425Sdougm 		return (1);
132*a3351425Sdougm 	}
133*a3351425Sdougm 
134*a3351425Sdougm 	/*
135*a3351425Sdougm 	 * Skip any datasets whose type does not match.
136*a3351425Sdougm 	 */
137*a3351425Sdougm 	if ((type & cbp->cb_types) == 0) {
1381cea05afSdougm 		zfs_close(zhp);
1391cea05afSdougm 		return (0);
1401cea05afSdougm 	}
1411cea05afSdougm 
1421cea05afSdougm 	if (cbp->cb_alloc == cbp->cb_used) {
1431cea05afSdougm 		zfs_handle_t **handles;
1441cea05afSdougm 
1451cea05afSdougm 		if (cbp->cb_alloc == 0)
1461cea05afSdougm 			cbp->cb_alloc = 64;
1471cea05afSdougm 		else
1481cea05afSdougm 			cbp->cb_alloc *= 2;
1491cea05afSdougm 
150*a3351425Sdougm 		handles = (zfs_handle_t **)calloc(1,
151*a3351425Sdougm 		    cbp->cb_alloc * sizeof (void *));
152*a3351425Sdougm 
1531cea05afSdougm 		if (handles == NULL) {
154*a3351425Sdougm 			zfs_close(zhp);
15557b448deSdougm 			return (0);
1561cea05afSdougm 		}
1571cea05afSdougm 		if (cbp->cb_handles) {
158*a3351425Sdougm 			bcopy(cbp->cb_handles, handles,
1591cea05afSdougm 			    cbp->cb_used * sizeof (void *));
1601cea05afSdougm 			free(cbp->cb_handles);
1611cea05afSdougm 		}
1621cea05afSdougm 
1631cea05afSdougm 		cbp->cb_handles = handles;
1641cea05afSdougm 	}
1651cea05afSdougm 
1661cea05afSdougm 	cbp->cb_handles[cbp->cb_used++] = zhp;
1671cea05afSdougm 
168*a3351425Sdougm 	return (0);
1691cea05afSdougm }
1701cea05afSdougm 
1711cea05afSdougm /*
1721cea05afSdougm  * get_all_filesystems(zfs_handle_t ***fslist, size_t *count)
1731cea05afSdougm  *
1741cea05afSdougm  * iterate through all ZFS file systems starting at the root. Returns
1751cea05afSdougm  * a count and an array of handle pointers. Allocating is only done
1761cea05afSdougm  * once. The caller does not need to free since it will be done at
1771cea05afSdougm  * sa_zfs_fini() time.
1781cea05afSdougm  */
1791cea05afSdougm 
1801cea05afSdougm static void
181549ec3ffSdougm get_all_filesystems(sa_handle_impl_t impl_handle,
182549ec3ffSdougm 			zfs_handle_t ***fslist, size_t *count)
1831cea05afSdougm {
1841cea05afSdougm 	get_all_cbdata_t cb = { 0 };
185*a3351425Sdougm 	cb.cb_types = ZFS_TYPE_FILESYSTEM;
1861cea05afSdougm 
187549ec3ffSdougm 	if (impl_handle->zfs_list != NULL) {
18857b448deSdougm 		*fslist = impl_handle->zfs_list;
18957b448deSdougm 		*count = impl_handle->zfs_list_count;
19057b448deSdougm 		return;
1911cea05afSdougm 	}
1921cea05afSdougm 
193549ec3ffSdougm 	(void) zfs_iter_root(impl_handle->zfs_libhandle,
19457b448deSdougm 	    get_one_filesystem, &cb);
1951cea05afSdougm 
196549ec3ffSdougm 	impl_handle->zfs_list = *fslist = cb.cb_handles;
197549ec3ffSdougm 	impl_handle->zfs_list_count = *count = cb.cb_used;
1981cea05afSdougm }
1991cea05afSdougm 
2006185db85Sdougm /*
2011cea05afSdougm  * mountpoint_compare(a, b)
2021cea05afSdougm  *
2031cea05afSdougm  * compares the mountpoint on two zfs file systems handles.
2041cea05afSdougm  * returns values following strcmp() model.
2056185db85Sdougm  */
2066185db85Sdougm 
2071cea05afSdougm static int
2081cea05afSdougm mountpoint_compare(const void *a, const void *b)
2091cea05afSdougm {
2101cea05afSdougm 	zfs_handle_t **za = (zfs_handle_t **)a;
2111cea05afSdougm 	zfs_handle_t **zb = (zfs_handle_t **)b;
2121cea05afSdougm 	char mounta[MAXPATHLEN];
2131cea05afSdougm 	char mountb[MAXPATHLEN];
2141cea05afSdougm 
2151cea05afSdougm 	verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
2161cea05afSdougm 	    sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
2171cea05afSdougm 	verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
2181cea05afSdougm 	    sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
2191cea05afSdougm 
2201cea05afSdougm 	return (strcmp(mounta, mountb));
2211cea05afSdougm }
2221cea05afSdougm 
2236185db85Sdougm /*
224549ec3ffSdougm  * get_zfs_dataset(impl_handle, path)
2256185db85Sdougm  *
2266185db85Sdougm  * get the name of the ZFS dataset the path is equivalent to.  The
2276185db85Sdougm  * dataset name is used for get/set of ZFS properties since libzfs
2286185db85Sdougm  * requires a dataset to do a zfs_open().
2296185db85Sdougm  */
2306185db85Sdougm 
2316185db85Sdougm static char *
232549ec3ffSdougm get_zfs_dataset(sa_handle_impl_t impl_handle, char *path)
2336185db85Sdougm {
2341cea05afSdougm 	size_t i, count = 0;
2356185db85Sdougm 	char *dataset = NULL;
2361cea05afSdougm 	zfs_handle_t **zlist;
2371cea05afSdougm 	char mountpoint[ZFS_MAXPROPLEN];
23867331909Sdougm 	char canmount[ZFS_MAXPROPLEN];
2396185db85Sdougm 
240549ec3ffSdougm 	get_all_filesystems(impl_handle, &zlist, &count);
2411cea05afSdougm 	qsort(zlist, count, sizeof (void *), mountpoint_compare);
2421cea05afSdougm 	for (i = 0; i < count; i++) {
24357b448deSdougm 		/* must have a mountpoint */
24457b448deSdougm 		if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint,
24557b448deSdougm 		    sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
24657b448deSdougm 			/* no mountpoint */
24757b448deSdougm 			continue;
24857b448deSdougm 		}
24957b448deSdougm 
25057b448deSdougm 		/* mountpoint must be a path */
25157b448deSdougm 		if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
25257b448deSdougm 		    strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0)
25357b448deSdougm 			continue;
25457b448deSdougm 
25557b448deSdougm 		/* canmount must be set */
25657b448deSdougm 		canmount[0] = '\0';
25757b448deSdougm 		if (!zfs_prop_get(zlist[i], ZFS_PROP_CANMOUNT, canmount,
25867331909Sdougm 		    sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 ||
25957b448deSdougm 		    strcmp(canmount, "off") == 0)
26057b448deSdougm 			continue;
2611cea05afSdougm 
26257b448deSdougm 		/*
26357b448deSdougm 		 * have a mountable handle but want to skip those marked none
26457b448deSdougm 		 * and legacy
26557b448deSdougm 		 */
26657b448deSdougm 		if (strcmp(mountpoint, path) == 0) {
26757b448deSdougm 			dataset = (char *)zfs_get_name(zlist[i]);
26857b448deSdougm 			break;
26957b448deSdougm 		}
2701cea05afSdougm 
2716185db85Sdougm 	}
2721cea05afSdougm 
27357b448deSdougm 	if (dataset != NULL)
27457b448deSdougm 		dataset = strdup(dataset);
27557b448deSdougm 
2766185db85Sdougm 	return (dataset);
2776185db85Sdougm }
2786185db85Sdougm 
2796185db85Sdougm /*
2806185db85Sdougm  * get_zfs_property(dataset, property)
2816185db85Sdougm  *
2826185db85Sdougm  * Get the file system property specified from the ZFS dataset.
2836185db85Sdougm  */
2846185db85Sdougm 
2856185db85Sdougm static char *
2866185db85Sdougm get_zfs_property(char *dataset, zfs_prop_t property)
2876185db85Sdougm {
2886185db85Sdougm 	zfs_handle_t *handle = NULL;
2896185db85Sdougm 	char shareopts[ZFS_MAXPROPLEN];
2906185db85Sdougm 	libzfs_handle_t *libhandle;
2916185db85Sdougm 
2926185db85Sdougm 	libhandle = libzfs_init();
2936185db85Sdougm 	if (libhandle != NULL) {
29457b448deSdougm 		handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
29557b448deSdougm 		if (handle != NULL) {
29657b448deSdougm 			if (zfs_prop_get(handle, property, shareopts,
29757b448deSdougm 			    sizeof (shareopts), NULL, NULL, 0,
29857b448deSdougm 			    B_FALSE) == 0) {
29957b448deSdougm 				zfs_close(handle);
30057b448deSdougm 				libzfs_fini(libhandle);
30157b448deSdougm 				return (strdup(shareopts));
30257b448deSdougm 			}
30357b448deSdougm 			zfs_close(handle);
3046185db85Sdougm 		}
30557b448deSdougm 		libzfs_fini(libhandle);
3066185db85Sdougm 	}
3076185db85Sdougm 	return (NULL);
3086185db85Sdougm }
3096185db85Sdougm 
3106185db85Sdougm /*
311549ec3ffSdougm  * sa_zfs_is_shared(handle, path)
3126185db85Sdougm  *
3136185db85Sdougm  * Check to see if the ZFS path provided has the sharenfs option set
3146185db85Sdougm  * or not.
3156185db85Sdougm  */
3166185db85Sdougm 
3176185db85Sdougm int
318549ec3ffSdougm sa_zfs_is_shared(sa_handle_t sahandle, char *path)
3196185db85Sdougm {
3206185db85Sdougm 	int ret = 0;
3216185db85Sdougm 	char *dataset;
3226185db85Sdougm 	zfs_handle_t *handle = NULL;
3236185db85Sdougm 	char shareopts[ZFS_MAXPROPLEN];
3246185db85Sdougm 	libzfs_handle_t *libhandle;
3256185db85Sdougm 
326549ec3ffSdougm 	dataset = get_zfs_dataset((sa_handle_t)sahandle, path);
3276185db85Sdougm 	if (dataset != NULL) {
32857b448deSdougm 		libhandle = libzfs_init();
32957b448deSdougm 		if (libhandle != NULL) {
33057b448deSdougm 			handle = zfs_open(libhandle, dataset,
33157b448deSdougm 			    ZFS_TYPE_FILESYSTEM);
33257b448deSdougm 			if (handle != NULL) {
33357b448deSdougm 				if (zfs_prop_get(handle, ZFS_PROP_SHARENFS,
33457b448deSdougm 				    shareopts, sizeof (shareopts), NULL, NULL,
33557b448deSdougm 				    0, B_FALSE) == 0 &&
33657b448deSdougm 				    strcmp(shareopts, "off") != 0) {
33757b448deSdougm 					ret = 1; /* it is shared */
33857b448deSdougm 				}
33957b448deSdougm 				zfs_close(handle);
34057b448deSdougm 			}
34157b448deSdougm 			libzfs_fini(libhandle);
3426185db85Sdougm 		}
34357b448deSdougm 		free(dataset);
3446185db85Sdougm 	}
3456185db85Sdougm 	return (ret);
3466185db85Sdougm }
3476185db85Sdougm 
3486185db85Sdougm /*
3496185db85Sdougm  * find_or_create_group(groupname, proto, *err)
3506185db85Sdougm  *
3516185db85Sdougm  * While walking the ZFS tree, we need to add shares to a defined
3526185db85Sdougm  * group. If the group doesn't exist, create it first, making sure it
3536185db85Sdougm  * is marked as a ZFS group.
3546185db85Sdougm  *
35593a6f655Sdougm  * Note that all ZFS shares are in a subgroup of the top level group
35693a6f655Sdougm  * called "zfs".
3576185db85Sdougm  */
3586185db85Sdougm 
3596185db85Sdougm static sa_group_t
360549ec3ffSdougm find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err)
3616185db85Sdougm {
3626185db85Sdougm 	sa_group_t group;
3636185db85Sdougm 	sa_optionset_t optionset;
3646185db85Sdougm 	int ret = SA_OK;
3656185db85Sdougm 
3666185db85Sdougm 	/*
3676185db85Sdougm 	 * we check to see if the "zfs" group exists. Since this
3686185db85Sdougm 	 * should be the top level group, we don't want the
3696185db85Sdougm 	 * parent. This is to make sure the zfs group has been created
3706185db85Sdougm 	 * and to created if it hasn't been.
3716185db85Sdougm 	 */
372549ec3ffSdougm 	group = sa_get_group(handle, groupname);
3736185db85Sdougm 	if (group == NULL) {
37457b448deSdougm 		group = sa_create_group(handle, groupname, &ret);
37593a6f655Sdougm 
37657b448deSdougm 		/* make sure this is flagged as a ZFS group */
37757b448deSdougm 		if (group != NULL)
37857b448deSdougm 			ret = sa_set_group_attr(group, "zfs", "true");
3796185db85Sdougm 	}
3806185db85Sdougm 	if (group != NULL) {
38157b448deSdougm 		if (proto != NULL) {
38257b448deSdougm 			optionset = sa_get_optionset(group, proto);
38357b448deSdougm 			if (optionset == NULL) {
38457b448deSdougm 				optionset = sa_create_optionset(group, proto);
38557b448deSdougm 			} else {
38657b448deSdougm 				char **protolist;
38757b448deSdougm 				int numprotos, i;
38857b448deSdougm 				numprotos = sa_get_protocols(&protolist);
38957b448deSdougm 				for (i = 0; i < numprotos; i++) {
39057b448deSdougm 					optionset = sa_create_optionset(group,
39157b448deSdougm 					    protolist[i]);
39257b448deSdougm 				}
39357b448deSdougm 				if (protolist != NULL)
39457b448deSdougm 					free(protolist);
39557b448deSdougm 			}
3966185db85Sdougm 		}
3976185db85Sdougm 	}
3986185db85Sdougm 	if (err != NULL)
39957b448deSdougm 		*err = ret;
4006185db85Sdougm 	return (group);
4016185db85Sdougm }
4026185db85Sdougm 
40393a6f655Sdougm /*
40493a6f655Sdougm  * find_or_create_zfs_subgroup(groupname, optstring, *err)
40593a6f655Sdougm  *
40693a6f655Sdougm  * ZFS shares will be in a subgroup of the "zfs" master group.  This
40793a6f655Sdougm  * function looks to see if the groupname exists and returns it if it
40893a6f655Sdougm  * does or else creates a new one with the specified name and returns
40993a6f655Sdougm  * that.  The "zfs" group will exist before we get here, but we make
41093a6f655Sdougm  * sure just in case.
41193a6f655Sdougm  *
41293a6f655Sdougm  * err must be a valid pointer.
41393a6f655Sdougm  */
41493a6f655Sdougm 
41593a6f655Sdougm static sa_group_t
416549ec3ffSdougm find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname,
417549ec3ffSdougm 				char *optstring, int *err)
41893a6f655Sdougm {
41993a6f655Sdougm 	sa_group_t group = NULL;
42093a6f655Sdougm 	sa_group_t zfs;
42193a6f655Sdougm 	char *name;
42293a6f655Sdougm 	char *options;
42393a6f655Sdougm 
42493a6f655Sdougm 	/* start with the top-level "zfs" group */
425549ec3ffSdougm 	zfs = sa_get_group(handle, "zfs");
42693a6f655Sdougm 	*err = SA_OK;
42793a6f655Sdougm 	if (zfs != NULL) {
42857b448deSdougm 		for (group = sa_get_sub_group(zfs); group != NULL;
42957b448deSdougm 		    group = sa_get_next_group(group)) {
43057b448deSdougm 			name = sa_get_group_attr(group, "name");
43157b448deSdougm 			if (name != NULL && strcmp(name, groupname) == 0) {
43257b448deSdougm 				/* have the group so break out of here */
43357b448deSdougm 				sa_free_attr_string(name);
43457b448deSdougm 				break;
43557b448deSdougm 			}
43657b448deSdougm 			if (name != NULL)
43757b448deSdougm 				sa_free_attr_string(name);
43893a6f655Sdougm 		}
43957b448deSdougm 
44057b448deSdougm 		if (group == NULL) {
44157b448deSdougm 			/*
44257b448deSdougm 			 * need to create the sub-group since it doesn't exist
44357b448deSdougm 			 */
44457b448deSdougm 			group = _sa_create_zfs_group(zfs, groupname);
44557b448deSdougm 			if (group != NULL)
44657b448deSdougm 				set_node_attr(group, "zfs", "true");
44757b448deSdougm 			if (strcmp(optstring, "on") == 0)
44857b448deSdougm 				optstring = "rw";
44957b448deSdougm 			if (group != NULL) {
45057b448deSdougm 				options = strdup(optstring);
45157b448deSdougm 				if (options != NULL) {
45257b448deSdougm 					*err = sa_parse_legacy_options(group,
45357b448deSdougm 					    options, "nfs");
45457b448deSdougm 					free(options);
45557b448deSdougm 				} else {
45657b448deSdougm 					*err = SA_NO_MEMORY;
45757b448deSdougm 				}
45857b448deSdougm 			}
45993a6f655Sdougm 		}
46093a6f655Sdougm 	}
46193a6f655Sdougm 	return (group);
46293a6f655Sdougm }
46393a6f655Sdougm 
46457b448deSdougm /*
46557b448deSdougm  * zfs_inherited(handle, source, sourcestr)
46657b448deSdougm  *
46757b448deSdougm  * handle case of inherited sharenfs. Pulled out of sa_get_zfs_shares
46857b448deSdougm  * for readability.
46957b448deSdougm  */
47057b448deSdougm static int
47157b448deSdougm zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr,
47257b448deSdougm     char *shareopts, char *mountpoint)
47357b448deSdougm {
47457b448deSdougm 	int doshopt = 0;
47557b448deSdougm 	int err = SA_OK;
47657b448deSdougm 	sa_group_t group;
47757b448deSdougm 
47857b448deSdougm 	/*
47957b448deSdougm 	 * Need to find the "real" parent sub-group. It may not be
48057b448deSdougm 	 * mounted, but it was identified in the "sourcestr"
48157b448deSdougm 	 * variable. The real parent not mounted can occur if
48257b448deSdougm 	 * "canmount=off and sharenfs=on".
48357b448deSdougm 	 */
48457b448deSdougm 	group = find_or_create_zfs_subgroup(handle, sourcestr, shareopts,
48557b448deSdougm 	    &doshopt);
48657b448deSdougm 	if (group != NULL) {
48757b448deSdougm 		share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT,
48857b448deSdougm 		    &err);
48957b448deSdougm 		/*
49057b448deSdougm 		 * some options may only be on shares. If the opt
49157b448deSdougm 		 * string contains one of those, we put it just on the
49257b448deSdougm 		 * share.
49357b448deSdougm 		 */
49457b448deSdougm 		if (share != NULL && doshopt == SA_PROP_SHARE_ONLY) {
49557b448deSdougm 			char *options;
49657b448deSdougm 			options = strdup(shareopts);
49757b448deSdougm 			if (options != NULL) {
49857b448deSdougm 				err = sa_parse_legacy_options(share, options,
49957b448deSdougm 				    "nfs");
50057b448deSdougm 				free(options);
50157b448deSdougm 			}
50257b448deSdougm 		}
50357b448deSdougm 	} else {
50457b448deSdougm 		err = SA_NO_MEMORY;
50557b448deSdougm 	}
50657b448deSdougm 	return (err);
50757b448deSdougm }
50857b448deSdougm 
50957b448deSdougm /*
51057b448deSdougm  * zfs_notinherited()
51157b448deSdougm  *
51257b448deSdougm  * handle case where this is the top of a sub-group in ZFS. Pulled out
51357b448deSdougm  * of sa_get_zfs_shares for readability.
51457b448deSdougm  */
51557b448deSdougm static int
51657b448deSdougm zfs_notinherited(sa_group_t group, char *mountpoint, char *shareopts)
51757b448deSdougm {
51857b448deSdougm 	int err = SA_OK;
51957b448deSdougm 	sa_share_t share;
52057b448deSdougm 
52157b448deSdougm 	set_node_attr(group, "zfs", "true");
52257b448deSdougm 	share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT, &err);
52357b448deSdougm 	if (err == SA_OK) {
52457b448deSdougm 		if (strcmp(shareopts, "on") != 0) {
52557b448deSdougm 			char *options;
52657b448deSdougm 			options = strdup(shareopts);
52757b448deSdougm 			if (options != NULL) {
52857b448deSdougm 				err = sa_parse_legacy_options(group, options,
52957b448deSdougm 				    "nfs");
53057b448deSdougm 				free(options);
53157b448deSdougm 			}
53257b448deSdougm 			if (err == SA_PROP_SHARE_ONLY) {
53357b448deSdougm 				/*
53457b448deSdougm 				 * Same as above, some properties may
53557b448deSdougm 				 * only be on shares, but due to the
53657b448deSdougm 				 * ZFS sub-groups being artificial, we
53757b448deSdougm 				 * sometimes get this and have to deal
53857b448deSdougm 				 * with it. We do it by attempting to
53957b448deSdougm 				 * put it on the share.
54057b448deSdougm 				 */
54157b448deSdougm 				options = strdup(shareopts);
542*a3351425Sdougm 				if (options != NULL) {
54357b448deSdougm 					err = sa_parse_legacy_options(share,
54457b448deSdougm 					    options, "nfs");
54557b448deSdougm 					free(options);
546*a3351425Sdougm 				}
54757b448deSdougm 			}
54857b448deSdougm 			/* unmark the share's changed state */
54957b448deSdougm 			set_node_attr(share, "changed", NULL);
55057b448deSdougm 		}
55157b448deSdougm 	}
55257b448deSdougm 	return (err);
55357b448deSdougm }
55457b448deSdougm 
55557b448deSdougm /*
55657b448deSdougm  * zfs_grp_error(err)
55757b448deSdougm  *
55857b448deSdougm  * Print group create error, but only once. If err is 0 do the
55957b448deSdougm  * print else don't.
56057b448deSdougm  */
56157b448deSdougm 
56257b448deSdougm static void
56357b448deSdougm zfs_grp_error(int err)
56457b448deSdougm {
56557b448deSdougm 	if (err == 0) {
56657b448deSdougm 		/* only print error once */
56757b448deSdougm 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
56857b448deSdougm 		    "Cannot create ZFS subgroup during initialization:"
56957b448deSdougm 		    " %s\n"), sa_errorstr(SA_SYSTEM_ERR));
57057b448deSdougm 	}
57157b448deSdougm }
57257b448deSdougm 
5736185db85Sdougm /*
574549ec3ffSdougm  * sa_get_zfs_shares(handle, groupname)
5756185db85Sdougm  *
5766185db85Sdougm  * Walk the mnttab for all zfs mounts and determine which are
5776185db85Sdougm  * shared. Find or create the appropriate group/sub-group to contain
5786185db85Sdougm  * the shares.
5796185db85Sdougm  *
5806185db85Sdougm  * All shares are in a sub-group that will hold the properties. This
5816185db85Sdougm  * allows representing the inherited property model.
5826185db85Sdougm  */
5836185db85Sdougm 
5846185db85Sdougm int
585549ec3ffSdougm sa_get_zfs_shares(sa_handle_t handle, char *groupname)
5866185db85Sdougm {
5876185db85Sdougm 	sa_group_t group;
5886185db85Sdougm 	sa_group_t zfsgroup;
5896185db85Sdougm 	int legacy = 0;
5906185db85Sdougm 	int err;
5911cea05afSdougm 	zfs_handle_t **zlist;
5926185db85Sdougm 	char shareopts[ZFS_MAXPROPLEN];
5936185db85Sdougm 	sa_share_t share;
5946185db85Sdougm 	zfs_source_t source;
5956185db85Sdougm 	char sourcestr[ZFS_MAXPROPLEN];
5961cea05afSdougm 	char mountpoint[ZFS_MAXPROPLEN];
5971cea05afSdougm 	size_t count = 0, i;
598549ec3ffSdougm 	libzfs_handle_t *zfs_libhandle;
5996185db85Sdougm 
6006185db85Sdougm 	/*
601549ec3ffSdougm 	 * If we can't access libzfs, don't bother doing anything.
6026185db85Sdougm 	 */
603549ec3ffSdougm 	zfs_libhandle = ((sa_handle_impl_t)handle)->zfs_libhandle;
6041cea05afSdougm 	if (zfs_libhandle == NULL)
60557b448deSdougm 		return (SA_SYSTEM_ERR);
6066185db85Sdougm 
607549ec3ffSdougm 	zfsgroup = find_or_create_group(handle, groupname, "nfs", &err);
6086185db85Sdougm 	if (zfsgroup != NULL) {
6096185db85Sdougm 		/*
6106185db85Sdougm 		 * need to walk the mounted ZFS pools and datasets to
6116185db85Sdougm 		 * find shares that are possible.
6126185db85Sdougm 		 */
61357b448deSdougm 		get_all_filesystems((sa_handle_impl_t)handle, &zlist, &count);
61457b448deSdougm 		qsort(zlist, count, sizeof (void *), mountpoint_compare);
61557b448deSdougm 
61657b448deSdougm 		group = zfsgroup;
61757b448deSdougm 		for (i = 0; i < count; i++) {
61857b448deSdougm 			char *dataset;
61957b448deSdougm 
62057b448deSdougm 			source = ZFS_SRC_ALL;
62157b448deSdougm 			if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT,
62257b448deSdougm 			    mountpoint, sizeof (mountpoint), NULL, NULL, 0,
62357b448deSdougm 			    B_FALSE) != 0) {
62457b448deSdougm 				/* no mountpoint */
62557b448deSdougm 				continue;
62657b448deSdougm 			}
6271cea05afSdougm 
6281cea05afSdougm 			/*
62957b448deSdougm 			 * zfs_get_name value must not be freed. It is just a
63057b448deSdougm 			 * pointer to a value in the handle.
6311cea05afSdougm 			 */
63257b448deSdougm 			if ((dataset = (char *)zfs_get_name(zlist[i])) == NULL)
63357b448deSdougm 				continue;
63457b448deSdougm 
6351cea05afSdougm 			/*
63657b448deSdougm 			 * only deal with "mounted" file systems since
63757b448deSdougm 			 * unmounted file systems can't actually be shared.
6381cea05afSdougm 			 */
63957b448deSdougm 
64057b448deSdougm 			if (!zfs_is_mounted(zlist[i], NULL))
6411cea05afSdougm 				continue;
64257b448deSdougm 
64357b448deSdougm 			if (zfs_prop_get(zlist[i], ZFS_PROP_SHARENFS, shareopts,
64457b448deSdougm 			    sizeof (shareopts), &source, sourcestr,
64557b448deSdougm 			    ZFS_MAXPROPLEN, B_FALSE) == 0 &&
64657b448deSdougm 			    strcmp(shareopts, "off") != 0) {
64757b448deSdougm 				/* it is shared so add to list */
64857b448deSdougm 				share = sa_find_share(handle, mountpoint);
64957b448deSdougm 				err = SA_OK;
65057b448deSdougm 				if (share != NULL) {
65193a6f655Sdougm 					/*
65257b448deSdougm 					 * A zfs file system had been shared
65357b448deSdougm 					 * through traditional methods
65457b448deSdougm 					 * (share/dfstab or added to a non-zfs
65557b448deSdougm 					 * group.  Now it has been added to a
65657b448deSdougm 					 * ZFS group via the zfs
65757b448deSdougm 					 * command. Remove from previous
65857b448deSdougm 					 * config and setup with current
65957b448deSdougm 					 * options.
66093a6f655Sdougm 					 */
66157b448deSdougm 					err = sa_remove_share(share);
66257b448deSdougm 					share = NULL;
66357b448deSdougm 				}
66457b448deSdougm 				if (err == SA_OK) {
66557b448deSdougm 					if (source & ZFS_SRC_INHERITED) {
66657b448deSdougm 						err = zfs_inherited(handle,
66757b448deSdougm 						    share, sourcestr,
66857b448deSdougm 						    shareopts, mountpoint);
66957b448deSdougm 					} else {
67057b448deSdougm 						group = _sa_create_zfs_group(
67157b448deSdougm 						    zfsgroup, dataset);
67257b448deSdougm 						if (group == NULL) {
67357b448deSdougm 							static int err = 0;
674*a3351425Sdougm 						/*
675*a3351425Sdougm 						 * there is a problem,
676*a3351425Sdougm 						 * but we can't do
677*a3351425Sdougm 						 * anything about it
678*a3351425Sdougm 						 * at this point so we
679*a3351425Sdougm 						 * issue a warning an
680*a3351425Sdougm 						 * move on.
681*a3351425Sdougm 						 */
68257b448deSdougm 							zfs_grp_error(err);
68357b448deSdougm 							err = 1;
68457b448deSdougm 							continue;
68557b448deSdougm 						}
68657b448deSdougm 						set_node_attr(group, "zfs",
68757b448deSdougm 						    "true");
68857b448deSdougm 						share = _sa_add_share(group,
68957b448deSdougm 						    mountpoint,
69057b448deSdougm 						    SA_SHARE_TRANSIENT, &err);
69157b448deSdougm 						err = zfs_notinherited(group,
69257b448deSdougm 						    mountpoint, shareopts);
69393a6f655Sdougm 					}
6946185db85Sdougm 				}
6956185db85Sdougm 			}
6966185db85Sdougm 		}
6976185db85Sdougm 	}
6981cea05afSdougm 	/*
6991cea05afSdougm 	 * Don't need to free the "zlist" variable since it is only a
7001cea05afSdougm 	 * pointer to a cached value that will be freed when
7011cea05afSdougm 	 * sa_fini() is called.
7021cea05afSdougm 	 */
7036185db85Sdougm 	return (legacy);
7046185db85Sdougm }
7056185db85Sdougm 
7066185db85Sdougm #define	COMMAND		"/usr/sbin/zfs"
7076185db85Sdougm 
7086185db85Sdougm /*
7096185db85Sdougm  * sa_zfs_set_sharenfs(group, path, on)
7106185db85Sdougm  *
7116185db85Sdougm  * Update the "sharenfs" property on the path. If on is true, then set
7126185db85Sdougm  * to the properties on the group or "on" if no properties are
7136185db85Sdougm  * defined. Set to "off" if on is false.
7146185db85Sdougm  */
7156185db85Sdougm 
7166185db85Sdougm int
7176185db85Sdougm sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
7186185db85Sdougm {
7196185db85Sdougm 	int ret = SA_NOT_IMPLEMENTED;
7206185db85Sdougm 	char *command;
7216185db85Sdougm 
7226185db85Sdougm 	command = malloc(ZFS_MAXPROPLEN * 2);
7236185db85Sdougm 	if (command != NULL) {
72457b448deSdougm 		char *opts = NULL;
72557b448deSdougm 		char *dataset = NULL;
72657b448deSdougm 		FILE *pfile;
72757b448deSdougm 		sa_handle_impl_t impl_handle;
72857b448deSdougm 		/* for now, NFS is always available for "zfs" */
72957b448deSdougm 		if (on) {
73057b448deSdougm 			opts = sa_proto_legacy_format("nfs", group, 1);
73157b448deSdougm 			if (opts != NULL && strlen(opts) == 0) {
73257b448deSdougm 				free(opts);
73357b448deSdougm 				opts = strdup("on");
73457b448deSdougm 			}
7356185db85Sdougm 		}
73657b448deSdougm 
73757b448deSdougm 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
73857b448deSdougm 		assert(impl_handle != NULL);
73957b448deSdougm 		if (impl_handle != NULL)
74057b448deSdougm 			dataset = get_zfs_dataset(impl_handle, path);
74157b448deSdougm 		else
7426185db85Sdougm 			ret = SA_SYSTEM_ERR;
74357b448deSdougm 
74457b448deSdougm 		if (dataset != NULL) {
74557b448deSdougm 			(void) snprintf(command, ZFS_MAXPROPLEN * 2,
74657b448deSdougm 			    "%s set sharenfs=\"%s\" %s", COMMAND,
74757b448deSdougm 			    opts != NULL ? opts : "off", dataset);
74857b448deSdougm 			pfile = popen(command, "r");
74957b448deSdougm 			if (pfile != NULL) {
75057b448deSdougm 				ret = pclose(pfile);
75157b448deSdougm 				if (ret != 0)
75257b448deSdougm 					ret = SA_SYSTEM_ERR;
75357b448deSdougm 			}
7546185db85Sdougm 		}
75557b448deSdougm 		if (opts != NULL)
75657b448deSdougm 			free(opts);
75757b448deSdougm 		if (dataset != NULL)
75857b448deSdougm 			free(dataset);
75957b448deSdougm 		free(command);
7606185db85Sdougm 	}
7616185db85Sdougm 	return (ret);
7626185db85Sdougm }
7636185db85Sdougm 
7646185db85Sdougm /*
7656185db85Sdougm  * sa_zfs_update(group)
7666185db85Sdougm  *
7676185db85Sdougm  * call back to ZFS to update the share if necessary.
7686185db85Sdougm  * Don't do it if it isn't a real change.
7696185db85Sdougm  */
7706185db85Sdougm int
7716185db85Sdougm sa_zfs_update(sa_group_t group)
7726185db85Sdougm {
7736185db85Sdougm 	sa_optionset_t protopt;
7746185db85Sdougm 	sa_group_t parent;
7756185db85Sdougm 	char *command;
7766185db85Sdougm 	char *optstring;
7776185db85Sdougm 	int ret = SA_OK;
7786185db85Sdougm 	int doupdate = 0;
7796185db85Sdougm 	FILE *pfile;
7806185db85Sdougm 
7816185db85Sdougm 	if (sa_is_share(group))
78257b448deSdougm 		parent = sa_get_parent_group(group);
7836185db85Sdougm 	else
78457b448deSdougm 		parent = group;
7856185db85Sdougm 
7866185db85Sdougm 	if (parent != NULL) {
78757b448deSdougm 		command = malloc(ZFS_MAXPROPLEN * 2);
78857b448deSdougm 		if (command == NULL)
78957b448deSdougm 			return (SA_NO_MEMORY);
79057b448deSdougm 
79157b448deSdougm 		*command = '\0';
79257b448deSdougm 		for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
79357b448deSdougm 		    protopt = sa_get_next_optionset(protopt)) {
79457b448deSdougm 
79557b448deSdougm 			char *proto = sa_get_optionset_attr(protopt, "type");
79657b448deSdougm 			char *path;
79757b448deSdougm 			char *dataset = NULL;
79857b448deSdougm 			char *zfsopts = NULL;
79957b448deSdougm 
80057b448deSdougm 			if (sa_is_share(group)) {
80157b448deSdougm 				path = sa_get_share_attr((sa_share_t)group,
80257b448deSdougm 				    "path");
80357b448deSdougm 				if (path != NULL) {
80457b448deSdougm 					sa_handle_impl_t impl_handle;
80557b448deSdougm 
80657b448deSdougm 					impl_handle = sa_find_group_handle(
80757b448deSdougm 					    group);
80857b448deSdougm 					if (impl_handle != NULL)
80957b448deSdougm 						dataset = get_zfs_dataset(
81057b448deSdougm 						    impl_handle, path);
81157b448deSdougm 					else
81257b448deSdougm 						ret = SA_SYSTEM_ERR;
81357b448deSdougm 
81457b448deSdougm 					sa_free_attr_string(path);
81557b448deSdougm 				}
8166185db85Sdougm 			} else {
81757b448deSdougm 				dataset = sa_get_group_attr(group, "name");
8186185db85Sdougm 			}
81957b448deSdougm 			/* update only when there is an optstring found */
82057b448deSdougm 			doupdate = 0;
82157b448deSdougm 			if (proto != NULL && dataset != NULL) {
82257b448deSdougm 				optstring = sa_proto_legacy_format(proto,
82357b448deSdougm 				    group, 1);
82457b448deSdougm 				zfsopts = get_zfs_property(dataset,
82557b448deSdougm 				    ZFS_PROP_SHARENFS);
82657b448deSdougm 
82757b448deSdougm 				if (optstring != NULL && zfsopts != NULL) {
82857b448deSdougm 					if (strcmp(optstring, zfsopts) != 0)
82957b448deSdougm 						doupdate++;
83057b448deSdougm 				}
83157b448deSdougm 
83257b448deSdougm 				if (doupdate) {
83357b448deSdougm 					if (optstring != NULL &&
83457b448deSdougm 					    strlen(optstring) > 0) {
83557b448deSdougm 						(void) snprintf(command,
83657b448deSdougm 						    ZFS_MAXPROPLEN * 2,
83757b448deSdougm 						    "%s set sharenfs=%s %s"
83857b448deSdougm 						    COMMAND,
83957b448deSdougm 						    optstring, dataset);
84057b448deSdougm 					} else {
84157b448deSdougm 						(void) snprintf(command,
84257b448deSdougm 						    ZFS_MAXPROPLEN * 2,
84357b448deSdougm 						    "%s set sharenfs=on %s",
84457b448deSdougm 						    COMMAND,
84557b448deSdougm 						    dataset);
84657b448deSdougm 					}
84757b448deSdougm 					pfile = popen(command, "r");
84857b448deSdougm 					if (pfile != NULL)
84957b448deSdougm 						ret = pclose(pfile);
85057b448deSdougm 					switch (ret) {
85157b448deSdougm 					default:
85257b448deSdougm 					case 1:
85357b448deSdougm 						ret = SA_SYSTEM_ERR;
85457b448deSdougm 						break;
85557b448deSdougm 					case 2:
85657b448deSdougm 						ret = SA_SYNTAX_ERR;
85757b448deSdougm 						break;
85857b448deSdougm 					case 0:
85957b448deSdougm 						break;
86057b448deSdougm 					}
86157b448deSdougm 				}
86257b448deSdougm 				if (optstring != NULL)
86357b448deSdougm 					free(optstring);
86457b448deSdougm 				if (zfsopts != NULL)
86557b448deSdougm 					free(zfsopts);
8666185db85Sdougm 			}
86757b448deSdougm 			if (proto != NULL)
86857b448deSdougm 				sa_free_attr_string(proto);
86957b448deSdougm 			if (dataset != NULL)
87057b448deSdougm 				free(dataset);
8716185db85Sdougm 		}
87257b448deSdougm 		free(command);
8736185db85Sdougm 	}
8746185db85Sdougm 	return (ret);
8756185db85Sdougm }
8766185db85Sdougm 
8776185db85Sdougm /*
8786185db85Sdougm  * sa_group_is_zfs(group)
8796185db85Sdougm  *
8806185db85Sdougm  * Given the group, determine if the zfs attribute is set.
8816185db85Sdougm  */
8826185db85Sdougm 
8836185db85Sdougm int
8846185db85Sdougm sa_group_is_zfs(sa_group_t group)
8856185db85Sdougm {
8866185db85Sdougm 	char *zfs;
8876185db85Sdougm 	int ret = 0;
8886185db85Sdougm 
8896185db85Sdougm 	zfs = sa_get_group_attr(group, "zfs");
8906185db85Sdougm 	if (zfs != NULL) {
89157b448deSdougm 		ret = 1;
89257b448deSdougm 		sa_free_attr_string(zfs);
8936185db85Sdougm 	}
8946185db85Sdougm 	return (ret);
8956185db85Sdougm }
8966185db85Sdougm 
8976185db85Sdougm /*
8986185db85Sdougm  * sa_path_is_zfs(path)
8996185db85Sdougm  *
9006185db85Sdougm  * Check to see if the file system path represents is of type "zfs".
9016185db85Sdougm  */
9026185db85Sdougm 
9036185db85Sdougm int
9046185db85Sdougm sa_path_is_zfs(char *path)
9056185db85Sdougm {
9066185db85Sdougm 	char *fstype;
9076185db85Sdougm 	int ret = 0;
9086185db85Sdougm 
9096185db85Sdougm 	fstype = sa_fstype(path);
91057b448deSdougm 	if (fstype != NULL && strcmp(fstype, "zfs") == 0)
91157b448deSdougm 		ret = 1;
9126185db85Sdougm 	if (fstype != NULL)
91357b448deSdougm 		sa_free_fstype(fstype);
9146185db85Sdougm 	return (ret);
9156185db85Sdougm }
916