13034Sdougm /*
23034Sdougm  * CDDL HEADER START
33034Sdougm  *
43034Sdougm  * The contents of this file are subject to the terms of the
53034Sdougm  * Common Development and Distribution License (the "License").
63034Sdougm  * You may not use this file except in compliance with the License.
73034Sdougm  *
83034Sdougm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93034Sdougm  * or http://www.opensolaris.org/os/licensing.
103034Sdougm  * See the License for the specific language governing permissions
113034Sdougm  * and limitations under the License.
123034Sdougm  *
133034Sdougm  * When distributing Covered Code, include this CDDL HEADER in each
143034Sdougm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153034Sdougm  * If applicable, add the following below this CDDL HEADER, with the
163034Sdougm  * fields enclosed by brackets "[]" replaced with your own identifying
173034Sdougm  * information: Portions Copyright [yyyy] [name of copyright owner]
183034Sdougm  *
193034Sdougm  * CDDL HEADER END
203034Sdougm  */
213034Sdougm 
223034Sdougm /*
233407Sdougm  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
243034Sdougm  * Use is subject to license terms.
253034Sdougm  */
263034Sdougm 
273034Sdougm #pragma ident	"%Z%%M%	%I%	%E% SMI"
283034Sdougm 
293034Sdougm #include <libzfs.h>
303034Sdougm #include <string.h>
313034Sdougm #include <libshare.h>
323034Sdougm #include "libshare_impl.h"
333218Sdougm #include <libintl.h>
343034Sdougm 
353034Sdougm extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *);
363034Sdougm extern sa_group_t _sa_create_zfs_group(sa_group_t, char *);
373034Sdougm extern char *sa_fstype(char *);
383034Sdougm extern void set_node_attr(void *, char *, char *);
393034Sdougm extern int sa_is_share(void *);
403218Sdougm 
413034Sdougm /*
423218Sdougm  * File system specific code for ZFS. The original code was stolen
433218Sdougm  * from the "zfs" command and modified to better suit this library's
443218Sdougm  * usage.
453218Sdougm  */
463218Sdougm 
473218Sdougm typedef struct get_all_cbdata {
483218Sdougm 	zfs_handle_t	**cb_handles;
493218Sdougm 	size_t		cb_alloc;
503218Sdougm 	size_t		cb_used;
513218Sdougm } get_all_cbdata_t;
523218Sdougm 
533218Sdougm /*
543910Sdougm  * sa_zfs_init(impl_handle)
553218Sdougm  *
563910Sdougm  * Initialize an access handle into libzfs.  The handle needs to stay
573910Sdougm  * around until sa_zfs_fini() in order to maintain the cache of
583910Sdougm  * mounts.
593218Sdougm  */
603218Sdougm 
613218Sdougm void
623910Sdougm sa_zfs_init(sa_handle_impl_t impl_handle)
633218Sdougm {
643910Sdougm 	impl_handle->zfs_libhandle = libzfs_init();
653910Sdougm 	libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE);
663218Sdougm }
673218Sdougm 
683218Sdougm /*
693910Sdougm  * sa_zfs_fini(impl_handle)
703218Sdougm  *
713218Sdougm  * cleanup data structures and the libzfs handle used for accessing
723218Sdougm  * zfs file share info.
733218Sdougm  */
743218Sdougm 
753218Sdougm void
763910Sdougm sa_zfs_fini(sa_handle_impl_t impl_handle)
773218Sdougm {
783910Sdougm 	if (impl_handle->zfs_libhandle != NULL) {
793910Sdougm 	    libzfs_fini(impl_handle->zfs_libhandle);
803910Sdougm 	    impl_handle->zfs_libhandle = NULL;
813910Sdougm 	    if (impl_handle->zfs_list != NULL) {
823218Sdougm 		/*
833218Sdougm 		 * contents of zfs_list were already freed by the call to
843218Sdougm 		 * libzfs_fini().
853218Sdougm 		 */
863910Sdougm 		free(impl_handle->zfs_list);
873910Sdougm 		impl_handle->zfs_list = NULL;
883910Sdougm 		impl_handle->zfs_list_count = 0;
893218Sdougm 	    }
903218Sdougm 	}
913218Sdougm }
923218Sdougm 
933218Sdougm /*
943218Sdougm  * get_one_filesystem(zfs_handle_t, data)
953218Sdougm  *
963218Sdougm  * an interator function called while iterating through the ZFS
973218Sdougm  * root. It accumulates into an array of file system handles that can
983218Sdougm  * be used to derive info about those file systems.
993034Sdougm  */
1003034Sdougm 
1013218Sdougm static int
1023218Sdougm get_one_filesystem(zfs_handle_t *zhp, void *data)
1033218Sdougm {
1043218Sdougm 	get_all_cbdata_t *cbp = data;
1053218Sdougm 
1063218Sdougm 	/*
1073218Sdougm 	 * Skip any zvols
1083218Sdougm 	 */
1093218Sdougm 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
1103218Sdougm 		zfs_close(zhp);
1113218Sdougm 		return (0);
1123218Sdougm 	}
1133218Sdougm 
1143218Sdougm 	if (cbp->cb_alloc == cbp->cb_used) {
1153218Sdougm 		zfs_handle_t **handles;
1163218Sdougm 
1173218Sdougm 		if (cbp->cb_alloc == 0)
1183218Sdougm 			cbp->cb_alloc = 64;
1193218Sdougm 		else
1203218Sdougm 			cbp->cb_alloc *= 2;
1213218Sdougm 
1223218Sdougm 		handles = calloc(1, cbp->cb_alloc * sizeof (void *));
1233218Sdougm 		if (handles == NULL) {
1243218Sdougm 		    return (0);
1253218Sdougm 		}
1263218Sdougm 
1273218Sdougm 		if (cbp->cb_handles) {
1283218Sdougm 			(void) memcpy(handles, cbp->cb_handles,
1293218Sdougm 			    cbp->cb_used * sizeof (void *));
1303218Sdougm 			free(cbp->cb_handles);
1313218Sdougm 		}
1323218Sdougm 
1333218Sdougm 		cbp->cb_handles = handles;
1343218Sdougm 	}
1353218Sdougm 
1363218Sdougm 	cbp->cb_handles[cbp->cb_used++] = zhp;
1373218Sdougm 
1383218Sdougm 	return (zfs_iter_filesystems(zhp, get_one_filesystem, data));
1393218Sdougm }
1403218Sdougm 
1413218Sdougm /*
1423218Sdougm  * get_all_filesystems(zfs_handle_t ***fslist, size_t *count)
1433218Sdougm  *
1443218Sdougm  * iterate through all ZFS file systems starting at the root. Returns
1453218Sdougm  * a count and an array of handle pointers. Allocating is only done
1463218Sdougm  * once. The caller does not need to free since it will be done at
1473218Sdougm  * sa_zfs_fini() time.
1483218Sdougm  */
1493218Sdougm 
1503218Sdougm static void
1513910Sdougm get_all_filesystems(sa_handle_impl_t impl_handle,
1523910Sdougm 			zfs_handle_t ***fslist, size_t *count)
1533218Sdougm {
1543218Sdougm 	get_all_cbdata_t cb = { 0 };
1553218Sdougm 
1563910Sdougm 	if (impl_handle->zfs_list != NULL) {
1573910Sdougm 	    *fslist = impl_handle->zfs_list;
1583910Sdougm 	    *count = impl_handle->zfs_list_count;
1593218Sdougm 	    return;
1603218Sdougm 	}
1613218Sdougm 
1623910Sdougm 	(void) zfs_iter_root(impl_handle->zfs_libhandle,
1633910Sdougm 				get_one_filesystem, &cb);
1643218Sdougm 
1653910Sdougm 	impl_handle->zfs_list = *fslist = cb.cb_handles;
1663910Sdougm 	impl_handle->zfs_list_count = *count = cb.cb_used;
1673218Sdougm }
1683218Sdougm 
1693218Sdougm /*
1703218Sdougm  * mountpoint_compare(a, b)
1713218Sdougm  *
1723218Sdougm  * compares the mountpoint on two zfs file systems handles.
1733218Sdougm  * returns values following strcmp() model.
1743218Sdougm  */
1753218Sdougm 
1763218Sdougm static int
1773218Sdougm mountpoint_compare(const void *a, const void *b)
1783218Sdougm {
1793218Sdougm 	zfs_handle_t **za = (zfs_handle_t **)a;
1803218Sdougm 	zfs_handle_t **zb = (zfs_handle_t **)b;
1813218Sdougm 	char mounta[MAXPATHLEN];
1823218Sdougm 	char mountb[MAXPATHLEN];
1833218Sdougm 
1843218Sdougm 	verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
1853218Sdougm 	    sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
1863218Sdougm 	verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
1873218Sdougm 	    sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
1883218Sdougm 
1893218Sdougm 	return (strcmp(mounta, mountb));
1903218Sdougm }
1913218Sdougm 
1923034Sdougm /*
1933910Sdougm  * get_zfs_dataset(impl_handle, path)
1943034Sdougm  *
1953034Sdougm  * get the name of the ZFS dataset the path is equivalent to.  The
1963034Sdougm  * dataset name is used for get/set of ZFS properties since libzfs
1973034Sdougm  * requires a dataset to do a zfs_open().
1983034Sdougm  */
1993034Sdougm 
2003034Sdougm static char *
2013910Sdougm get_zfs_dataset(sa_handle_impl_t impl_handle, char *path)
2023034Sdougm {
2033218Sdougm 	size_t i, count = 0;
2043034Sdougm 	char *dataset = NULL;
2053218Sdougm 	zfs_handle_t **zlist;
2063218Sdougm 	char mountpoint[ZFS_MAXPROPLEN];
207*4180Sdougm 	char canmount[ZFS_MAXPROPLEN];
2083218Sdougm 
2093910Sdougm 	get_all_filesystems(impl_handle, &zlist, &count);
2103218Sdougm 	qsort(zlist, count, sizeof (void *), mountpoint_compare);
2113218Sdougm 	for (i = 0; i < count; i++) {
2123218Sdougm 	    /* must have a mountpoint */
2133218Sdougm 	    if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint,
2143218Sdougm 		sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
2153218Sdougm 		/* no mountpoint */
2163218Sdougm 		continue;
2173218Sdougm 	    }
2183034Sdougm 
2193218Sdougm 	    /* mountpoint must be a path */
2203218Sdougm 	    if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
2213218Sdougm 		strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0)
2223218Sdougm 		continue;
2233218Sdougm 
2243218Sdougm 	    /* canmount must be set */
225*4180Sdougm 	    canmount[0] = '\0';
226*4180Sdougm 	    if (!zfs_prop_get(zlist[i], ZFS_PROP_CANMOUNT, canmount,
227*4180Sdougm 		    sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 ||
228*4180Sdougm 		strcmp(canmount, "off") == 0)
2293218Sdougm 		continue;
2303218Sdougm 
2313218Sdougm 	/*
2323218Sdougm 	 * have a mountable handle but want to skip those marked none
2333218Sdougm 	 * and legacy
2343218Sdougm 	 */
2353218Sdougm 	    if (strcmp(mountpoint, path) == 0) {
2363218Sdougm 		dataset = (char *)zfs_get_name(zlist[i]);
2373218Sdougm 		break;
2383034Sdougm 	    }
2393218Sdougm 
2403034Sdougm 	}
2413218Sdougm 
2423034Sdougm 	if (dataset != NULL) {
2433034Sdougm 	    dataset = strdup(dataset);
2443034Sdougm 	}
2453034Sdougm 	return (dataset);
2463034Sdougm }
2473034Sdougm 
2483034Sdougm /*
2493034Sdougm  * get_zfs_property(dataset, property)
2503034Sdougm  *
2513034Sdougm  * Get the file system property specified from the ZFS dataset.
2523034Sdougm  */
2533034Sdougm 
2543034Sdougm static char *
2553034Sdougm get_zfs_property(char *dataset, zfs_prop_t property)
2563034Sdougm {
2573034Sdougm 	zfs_handle_t *handle = NULL;
2583034Sdougm 	char shareopts[ZFS_MAXPROPLEN];
2593034Sdougm 	libzfs_handle_t *libhandle;
2603034Sdougm 
2613034Sdougm 	libhandle = libzfs_init();
2623034Sdougm 	if (libhandle != NULL) {
2633034Sdougm 	    handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
2643034Sdougm 	    if (handle != NULL) {
2653034Sdougm 		if (zfs_prop_get(handle, property, shareopts,
2663034Sdougm 				sizeof (shareopts), NULL, NULL, 0,
2673218Sdougm 				B_FALSE) == 0) {
2683034Sdougm 		    zfs_close(handle);
2693034Sdougm 		    libzfs_fini(libhandle);
2703034Sdougm 		    return (strdup(shareopts));
2713034Sdougm 		}
2723034Sdougm 		zfs_close(handle);
2733034Sdougm 	    }
2743034Sdougm 	    libzfs_fini(libhandle);
2753034Sdougm 	}
2763034Sdougm 	return (NULL);
2773034Sdougm }
2783034Sdougm 
2793034Sdougm /*
2803910Sdougm  * sa_zfs_is_shared(handle, path)
2813034Sdougm  *
2823034Sdougm  * Check to see if the ZFS path provided has the sharenfs option set
2833034Sdougm  * or not.
2843034Sdougm  */
2853034Sdougm 
2863034Sdougm int
2873910Sdougm sa_zfs_is_shared(sa_handle_t sahandle, char *path)
2883034Sdougm {
2893034Sdougm 	int ret = 0;
2903034Sdougm 	char *dataset;
2913034Sdougm 	zfs_handle_t *handle = NULL;
2923034Sdougm 	char shareopts[ZFS_MAXPROPLEN];
2933034Sdougm 	libzfs_handle_t *libhandle;
2943034Sdougm 
2953910Sdougm 	dataset = get_zfs_dataset((sa_handle_t)sahandle, path);
2963034Sdougm 	if (dataset != NULL) {
2973034Sdougm 	    libhandle = libzfs_init();
2983034Sdougm 	    if (libhandle != NULL) {
2993034Sdougm 		handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
3003034Sdougm 		if (handle != NULL) {
3013034Sdougm 		    if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts,
3023034Sdougm 					sizeof (shareopts), NULL, NULL, 0,
3033218Sdougm 					B_FALSE) == 0 &&
3043034Sdougm 			strcmp(shareopts, "off") != 0)
3053034Sdougm 			ret = 1; /* it is shared */
3063034Sdougm 		    zfs_close(handle);
3073034Sdougm 		}
3083034Sdougm 		libzfs_fini(libhandle);
3093034Sdougm 	    }
3103034Sdougm 	    free(dataset);
3113034Sdougm 	}
3123034Sdougm 	return (ret);
3133034Sdougm }
3143034Sdougm 
3153034Sdougm /*
3163034Sdougm  * find_or_create_group(groupname, proto, *err)
3173034Sdougm  *
3183034Sdougm  * While walking the ZFS tree, we need to add shares to a defined
3193034Sdougm  * group. If the group doesn't exist, create it first, making sure it
3203034Sdougm  * is marked as a ZFS group.
3213034Sdougm  *
3223108Sdougm  * Note that all ZFS shares are in a subgroup of the top level group
3233108Sdougm  * called "zfs".
3243034Sdougm  */
3253034Sdougm 
3263034Sdougm static sa_group_t
3273910Sdougm find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err)
3283034Sdougm {
3293034Sdougm 	sa_group_t group;
3303034Sdougm 	sa_optionset_t optionset;
3313034Sdougm 	int ret = SA_OK;
3323034Sdougm 
3333034Sdougm 	/*
3343034Sdougm 	 * we check to see if the "zfs" group exists. Since this
3353034Sdougm 	 * should be the top level group, we don't want the
3363034Sdougm 	 * parent. This is to make sure the zfs group has been created
3373034Sdougm 	 * and to created if it hasn't been.
3383034Sdougm 	 */
3393910Sdougm 	group = sa_get_group(handle, groupname);
3403034Sdougm 	if (group == NULL) {
3413910Sdougm 	    group = sa_create_group(handle, groupname, &ret);
3423108Sdougm 
3433108Sdougm 	    /* make sure this is flagged as a ZFS group */
3443034Sdougm 	    if (group != NULL)
3453034Sdougm 		ret = sa_set_group_attr(group, "zfs", "true");
3463034Sdougm 	}
3473034Sdougm 	if (group != NULL) {
3483034Sdougm 	    if (proto != NULL) {
3493034Sdougm 		optionset = sa_get_optionset(group, proto);
3503034Sdougm 		if (optionset == NULL) {
3513034Sdougm 		    optionset = sa_create_optionset(group, proto);
3523034Sdougm 		} else {
3533034Sdougm 		    char **protolist;
3543034Sdougm 		    int numprotos, i;
3553034Sdougm 		    numprotos = sa_get_protocols(&protolist);
3563034Sdougm 		    for (i = 0; i < numprotos; i++) {
3573034Sdougm 			optionset = sa_create_optionset(group, protolist[i]);
3583034Sdougm 		    }
3593034Sdougm 		    if (protolist != NULL)
3603034Sdougm 			free(protolist);
3613034Sdougm 		}
3623034Sdougm 	    }
3633034Sdougm 	}
3643034Sdougm 	if (err != NULL)
3653034Sdougm 	    *err = ret;
3663034Sdougm 	return (group);
3673034Sdougm }
3683034Sdougm 
3693034Sdougm /*
3703108Sdougm  * find_or_create_zfs_subgroup(groupname, optstring, *err)
3713108Sdougm  *
3723108Sdougm  * ZFS shares will be in a subgroup of the "zfs" master group.  This
3733108Sdougm  * function looks to see if the groupname exists and returns it if it
3743108Sdougm  * does or else creates a new one with the specified name and returns
3753108Sdougm  * that.  The "zfs" group will exist before we get here, but we make
3763108Sdougm  * sure just in case.
3773108Sdougm  *
3783108Sdougm  * err must be a valid pointer.
3793108Sdougm  */
3803108Sdougm 
3813108Sdougm static sa_group_t
3823910Sdougm find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname,
3833910Sdougm 				char *optstring, int *err)
3843108Sdougm {
3853108Sdougm 	sa_group_t group = NULL;
3863108Sdougm 	sa_group_t zfs;
3873108Sdougm 	char *name;
3883108Sdougm 	char *options;
3893108Sdougm 
3903108Sdougm 	/* start with the top-level "zfs" group */
3913910Sdougm 	zfs = sa_get_group(handle, "zfs");
3923108Sdougm 	*err = SA_OK;
3933108Sdougm 	if (zfs != NULL) {
3943108Sdougm 	    for (group = sa_get_sub_group(zfs); group != NULL;
3953108Sdougm 		group = sa_get_next_group(group)) {
3963108Sdougm 		name = sa_get_group_attr(group, "name");
3973108Sdougm 		if (name != NULL && strcmp(name, groupname) == 0) {
3983108Sdougm 		    /* have the group so break out of here */
3993108Sdougm 		    sa_free_attr_string(name);
4003108Sdougm 		    break;
4013108Sdougm 		}
4023108Sdougm 		if (name != NULL)
4033108Sdougm 		    sa_free_attr_string(name);
4043108Sdougm 	    }
4053108Sdougm 
4063108Sdougm 	    if (group == NULL) {
4073108Sdougm 		/* need to create the sub-group since it doesn't exist */
4083108Sdougm 		group = _sa_create_zfs_group(zfs, groupname);
4093108Sdougm 		if (group != NULL) {
4103108Sdougm 		    set_node_attr(group, "zfs", "true");
4113108Sdougm 		}
4123108Sdougm 		if (strcmp(optstring, "on") == 0)
4133108Sdougm 		    optstring = "rw";
4143108Sdougm 		if (group != NULL) {
4153108Sdougm 		    options = strdup(optstring);
4163108Sdougm 		    if (options != NULL) {
4173108Sdougm 			*err = sa_parse_legacy_options(group, options, "nfs");
4183108Sdougm 			free(options);
4193108Sdougm 		    } else {
4203108Sdougm 			*err = SA_NO_MEMORY;
4213108Sdougm 		    }
4223108Sdougm 		}
4233108Sdougm 	    }
4243108Sdougm 	}
4253108Sdougm 	return (group);
4263108Sdougm }
4273108Sdougm 
4283108Sdougm /*
4293910Sdougm  * sa_get_zfs_shares(handle, groupname)
4303034Sdougm  *
4313034Sdougm  * Walk the mnttab for all zfs mounts and determine which are
4323034Sdougm  * shared. Find or create the appropriate group/sub-group to contain
4333034Sdougm  * the shares.
4343034Sdougm  *
4353034Sdougm  * All shares are in a sub-group that will hold the properties. This
4363034Sdougm  * allows representing the inherited property model.
4373034Sdougm  */
4383034Sdougm 
4393034Sdougm int
4403910Sdougm sa_get_zfs_shares(sa_handle_t handle, char *groupname)
4413034Sdougm {
4423034Sdougm 	sa_group_t group;
4433034Sdougm 	sa_group_t zfsgroup;
4443034Sdougm 	int legacy = 0;
4453034Sdougm 	int err;
4463218Sdougm 	zfs_handle_t **zlist;
4473034Sdougm 	char shareopts[ZFS_MAXPROPLEN];
4483034Sdougm 	sa_share_t share;
4493034Sdougm 	zfs_source_t source;
4503034Sdougm 	char sourcestr[ZFS_MAXPROPLEN];
4513218Sdougm 	char mountpoint[ZFS_MAXPROPLEN];
4523108Sdougm 	char *options;
4533218Sdougm 	size_t count = 0, i;
4543910Sdougm 	libzfs_handle_t *zfs_libhandle;
4553034Sdougm 
4563034Sdougm 	/*
4573910Sdougm 	 * If we can't access libzfs, don't bother doing anything.
4583034Sdougm 	 */
4593910Sdougm 	zfs_libhandle = ((sa_handle_impl_t)handle)->zfs_libhandle;
4603218Sdougm 	if (zfs_libhandle == NULL)
4613034Sdougm 	    return (SA_SYSTEM_ERR);
4623034Sdougm 
4633910Sdougm 	zfsgroup = find_or_create_group(handle, groupname, "nfs", &err);
4643034Sdougm 	if (zfsgroup != NULL) {
4653034Sdougm 		/*
4663034Sdougm 		 * need to walk the mounted ZFS pools and datasets to
4673034Sdougm 		 * find shares that are possible.
4683034Sdougm 		 */
4693910Sdougm 	    get_all_filesystems((sa_handle_impl_t)handle, &zlist, &count);
4703218Sdougm 	    qsort(zlist, count, sizeof (void *), mountpoint_compare);
4713218Sdougm 
4723034Sdougm 	    group = zfsgroup;
4733218Sdougm 	    for (i = 0; i < count; i++) {
4743218Sdougm 		char *dataset;
4753218Sdougm 
4763218Sdougm 		source = ZFS_SRC_ALL;
4773218Sdougm 		if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint,
4783218Sdougm 					sizeof (mountpoint), NULL, NULL, 0,
4793218Sdougm 					B_FALSE) != 0) {
4803218Sdougm 		    /* no mountpoint */
4813218Sdougm 		    continue;
4823218Sdougm 		}
4833218Sdougm 
4843218Sdougm 		/*
4853218Sdougm 		 * zfs_get_name value must not be freed. It is just a
4863218Sdougm 		 * pointer to a value in the handle.
4873218Sdougm 		 */
4883218Sdougm 		if ((dataset = (char *)zfs_get_name(zlist[i])) == NULL)
4893218Sdougm 		    continue;
4903218Sdougm 
4913218Sdougm 		/*
4923218Sdougm 		 * only deal with "mounted" file systems since
4933218Sdougm 		 * unmounted file systems can't actually be shared.
4943218Sdougm 		 */
4953218Sdougm 
4963218Sdougm 		if (!zfs_is_mounted(zlist[i], NULL))
4973218Sdougm 		    continue;
4983218Sdougm 
4993218Sdougm 		if (zfs_prop_get(zlist[i], ZFS_PROP_SHARENFS, shareopts,
5003034Sdougm 					sizeof (shareopts), &source, sourcestr,
5013034Sdougm 					ZFS_MAXPROPLEN,
5023218Sdougm 					B_FALSE) == 0 &&
5033034Sdougm 			strcmp(shareopts, "off") != 0) {
5043218Sdougm 		    /* it is shared so add to list */
5053910Sdougm 		    share = sa_find_share(handle, mountpoint);
5063218Sdougm 		    err = SA_OK;
5073218Sdougm 		    if (share != NULL) {
5083218Sdougm 			/*
5093218Sdougm 			 * A zfs file system had been shared
5103218Sdougm 			 * through traditional methods
5113218Sdougm 			 * (share/dfstab or added to a non-zfs
5123218Sdougm 			 * group.  Now it has been added to a
5133218Sdougm 			 * ZFS group via the zfs
5143218Sdougm 			 * command. Remove from previous
5153218Sdougm 			 * config and setup with current
5163218Sdougm 			 * options.
5173218Sdougm 			 */
5183218Sdougm 			err = sa_remove_share(share);
5193218Sdougm 			share = NULL;
5203218Sdougm 		    }
5213218Sdougm 		    if (err == SA_OK) {
5223218Sdougm 			if (source & ZFS_SRC_INHERITED) {
5233218Sdougm 			    int doshopt = 0;
5243218Sdougm 			/*
5253218Sdougm 			 * Need to find the "real" parent
5263218Sdougm 			 * sub-group. It may not be mounted, but it
5273218Sdougm 			 * was identified in the "sourcestr"
5283218Sdougm 			 * variable. The real parent not mounted can
5293218Sdougm 			 * occur if "canmount=off and sharenfs=on".
5303218Sdougm 			 */
5313910Sdougm 			    group = find_or_create_zfs_subgroup(handle,
5323910Sdougm 							sourcestr,
5333218Sdougm 							shareopts, &doshopt);
5343218Sdougm 			    if (group != NULL) {
5353218Sdougm 				share = _sa_add_share(group, mountpoint,
5363034Sdougm 							SA_SHARE_TRANSIENT,
5373034Sdougm 							&err);
5383218Sdougm 				/*
5393218Sdougm 				 * some options may only be on
5403218Sdougm 				 * shares. If the opt string
5413218Sdougm 				 * contains one of those, we
5423218Sdougm 				 * put it just on the share.
5433218Sdougm 				 */
5443218Sdougm 				if (share != NULL &&
5453218Sdougm 				    doshopt == SA_PROP_SHARE_ONLY) {
5463218Sdougm 				    options = strdup(shareopts);
5473218Sdougm 				    if (options != NULL) {
5483218Sdougm 					err = sa_parse_legacy_options(share,
5493108Sdougm 								options, "nfs");
5503218Sdougm 					free(options);
5513108Sdougm 				    }
5523108Sdougm 				}
5533034Sdougm 			    } else {
5543218Sdougm 				err = SA_NO_MEMORY;
5553218Sdougm 			    }
5563218Sdougm 			} else {
5573218Sdougm 			    group = _sa_create_zfs_group(zfsgroup, dataset);
5583218Sdougm 			    if (group == NULL) {
5593218Sdougm 				static int err = 0;
5603108Sdougm 				/*
5613218Sdougm 				 * there is a problem, but we can't do
5623218Sdougm 				 * anything about it at this point so
5633218Sdougm 				 * we issue a warning an move on.
5643108Sdougm 				 */
5653218Sdougm 				if (err == 0) {
5663218Sdougm 				    /* only print error once */
5673218Sdougm 				    (void) fprintf(stderr,
5683407Sdougm 					dgettext(TEXT_DOMAIN,
5693407Sdougm 						"Cannot create ZFS subgroup "
5703218Sdougm 						"during initialization:"
5713218Sdougm 						" %s\n"),
5723218Sdougm 					sa_errorstr(SA_SYSTEM_ERR));
5733218Sdougm 				    err = 1;
5743218Sdougm 				}
5753218Sdougm 				continue;
5763218Sdougm 			    }
5773218Sdougm 			    set_node_attr(group, "zfs", "true");
5783218Sdougm 			    share = _sa_add_share(group, mountpoint,
5793218Sdougm 						SA_SHARE_TRANSIENT, &err);
5803218Sdougm 			    if (err == SA_OK) {
5813218Sdougm 				if (strcmp(shareopts, "on") != 0) {
5823218Sdougm 				    options = strdup(shareopts);
5833218Sdougm 				    if (options != NULL) {
5843218Sdougm 					err = sa_parse_legacy_options(group,
5853034Sdougm 									options,
5863034Sdougm 									"nfs");
5873218Sdougm 					free(options);
5883218Sdougm 				    }
5893218Sdougm 				    if (err == SA_PROP_SHARE_ONLY) {
5903108Sdougm 					/*
5913108Sdougm 					 * Same as above, some
5923108Sdougm 					 * properties may only be on
5933108Sdougm 					 * shares, but due to the ZFS
5943108Sdougm 					 * sub-groups being
5953108Sdougm 					 * artificial, we sometimes
5963108Sdougm 					 * get this and have to deal
5973108Sdougm 					 * with it. We do it by
5983108Sdougm 					 * attempting to put it on the
5993108Sdougm 					 * share.
6003108Sdougm 					 */
6013218Sdougm 					options = strdup(shareopts);
6023218Sdougm 					if (options != NULL)
6033218Sdougm 					    err = sa_parse_legacy_options(
6043108Sdougm 									share,
6053108Sdougm 									options,
6063108Sdougm 									"nfs");
6073108Sdougm 					    free(options);
6083108Sdougm 					}
6093218Sdougm 				    /* unmark the share's changed state */
6103218Sdougm 				    set_node_attr(share, "changed", NULL);
6113034Sdougm 				}
6123034Sdougm 			    }
6133034Sdougm 			}
6143034Sdougm 		    }
6153034Sdougm 		}
6163034Sdougm 	    }
6173034Sdougm 	}
6183218Sdougm 	/*
6193218Sdougm 	 * Don't need to free the "zlist" variable since it is only a
6203218Sdougm 	 * pointer to a cached value that will be freed when
6213218Sdougm 	 * sa_fini() is called.
6223218Sdougm 	 */
6233034Sdougm 	return (legacy);
6243034Sdougm }
6253034Sdougm 
6263034Sdougm #define	COMMAND		"/usr/sbin/zfs"
6273034Sdougm 
6283034Sdougm /*
6293034Sdougm  * sa_zfs_set_sharenfs(group, path, on)
6303034Sdougm  *
6313034Sdougm  * Update the "sharenfs" property on the path. If on is true, then set
6323034Sdougm  * to the properties on the group or "on" if no properties are
6333034Sdougm  * defined. Set to "off" if on is false.
6343034Sdougm  */
6353034Sdougm 
6363034Sdougm int
6373034Sdougm sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
6383034Sdougm {
6393034Sdougm 	int ret = SA_NOT_IMPLEMENTED;
6403034Sdougm 	char *command;
6413034Sdougm 
6423034Sdougm 	command = malloc(ZFS_MAXPROPLEN * 2);
6433034Sdougm 	if (command != NULL) {
6443034Sdougm 	    char *opts = NULL;
6453910Sdougm 	    char *dataset = NULL;
6463034Sdougm 	    FILE *pfile;
6473910Sdougm 	    sa_handle_impl_t impl_handle;
6483034Sdougm 	    /* for now, NFS is always available for "zfs" */
6493034Sdougm 	    if (on) {
6503034Sdougm 		opts = sa_proto_legacy_format("nfs", group, 1);
6513034Sdougm 		if (opts != NULL && strlen(opts) == 0) {
6523034Sdougm 		    free(opts);
6533034Sdougm 		    opts = strdup("on");
6543034Sdougm 		}
6553034Sdougm 	    }
6563910Sdougm 
6573910Sdougm 	    impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
658*4180Sdougm 	    assert(impl_handle != NULL);
6593910Sdougm 	    if (impl_handle != NULL)
6603910Sdougm 		dataset = get_zfs_dataset(impl_handle, path);
6613910Sdougm 	    else
6623910Sdougm 		ret = SA_SYSTEM_ERR;
6633910Sdougm 
6643034Sdougm 	    if (dataset != NULL) {
6653034Sdougm 		(void) snprintf(command, ZFS_MAXPROPLEN * 2,
6663034Sdougm 				"%s set sharenfs=\"%s\" %s", COMMAND,
6673034Sdougm 				opts != NULL ? opts : "off",
6683034Sdougm 				dataset);
6693034Sdougm 		pfile = popen(command, "r");
6703034Sdougm 		if (pfile != NULL) {
6713034Sdougm 		    ret = pclose(pfile);
6723034Sdougm 		    if (ret != 0)
6733034Sdougm 			ret = SA_SYSTEM_ERR;
6743034Sdougm 		}
6753034Sdougm 	    }
6763034Sdougm 	    if (opts != NULL)
6773034Sdougm 		free(opts);
6783034Sdougm 	    if (dataset != NULL)
6793034Sdougm 		free(dataset);
6803034Sdougm 	    free(command);
6813034Sdougm 	}
6823034Sdougm 	return (ret);
6833034Sdougm }
6843034Sdougm 
6853034Sdougm /*
6863034Sdougm  * sa_zfs_update(group)
6873034Sdougm  *
6883034Sdougm  * call back to ZFS to update the share if necessary.
6893034Sdougm  * Don't do it if it isn't a real change.
6903034Sdougm  */
6913034Sdougm int
6923034Sdougm sa_zfs_update(sa_group_t group)
6933034Sdougm {
6943034Sdougm 	sa_optionset_t protopt;
6953034Sdougm 	sa_group_t parent;
6963034Sdougm 	char *command;
6973034Sdougm 	char *optstring;
6983034Sdougm 	int ret = SA_OK;
6993034Sdougm 	int doupdate = 0;
7003034Sdougm 	FILE *pfile;
7013034Sdougm 
7023034Sdougm 	if (sa_is_share(group))
7033034Sdougm 	    parent = sa_get_parent_group(group);
7043034Sdougm 	else
7053034Sdougm 	    parent = group;
7063034Sdougm 
7073034Sdougm 	if (parent != NULL) {
7083034Sdougm 	    command = malloc(ZFS_MAXPROPLEN * 2);
7093034Sdougm 	    if (command == NULL)
7103034Sdougm 		return (SA_NO_MEMORY);
7113034Sdougm 
7123034Sdougm 	    *command = '\0';
7133034Sdougm 	    for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
7143034Sdougm 		protopt = sa_get_next_optionset(protopt)) {
7153034Sdougm 
7163034Sdougm 		char *proto = sa_get_optionset_attr(protopt, "type");
7173034Sdougm 		char *path;
7183034Sdougm 		char *dataset = NULL;
7193034Sdougm 		char *zfsopts = NULL;
7203034Sdougm 
7213034Sdougm 		if (sa_is_share(group)) {
7223034Sdougm 		    path = sa_get_share_attr((sa_share_t)group, "path");
7233034Sdougm 		    if (path != NULL) {
7243910Sdougm 			sa_handle_impl_t impl_handle;
7253910Sdougm 
7263910Sdougm 			impl_handle = sa_find_group_handle(group);
7273910Sdougm 			if (impl_handle != NULL)
7283910Sdougm 			    dataset = get_zfs_dataset(impl_handle, path);
7293910Sdougm 			else
7303910Sdougm 			    ret = SA_SYSTEM_ERR;
7313910Sdougm 
7323034Sdougm 			sa_free_attr_string(path);
7333034Sdougm 		    }
7343034Sdougm 		} else {
7353034Sdougm 		    dataset = sa_get_group_attr(group, "name");
7363034Sdougm 		}
7373034Sdougm 		/* update only when there is an optstring found */
7383034Sdougm 		doupdate = 0;
7393034Sdougm 		if (proto != NULL && dataset != NULL) {
7403034Sdougm 		    optstring = sa_proto_legacy_format(proto, group, 1);
7413034Sdougm 		    zfsopts = get_zfs_property(dataset, ZFS_PROP_SHARENFS);
7423034Sdougm 
7433034Sdougm 		    if (optstring != NULL && zfsopts != NULL) {
7443034Sdougm 			if (strcmp(optstring, zfsopts) != 0)
7453034Sdougm 			    doupdate++;
7463034Sdougm 		    }
7473034Sdougm 
7483034Sdougm 		    if (doupdate) {
7493034Sdougm 			if (optstring != NULL && strlen(optstring) > 0) {
7503034Sdougm 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
7513034Sdougm 					    "%s set sharenfs=%s %s", COMMAND,
7523034Sdougm 					    optstring, dataset);
7533034Sdougm 			} else {
7543034Sdougm 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
7553034Sdougm 					    "%s set sharenfs=on %s", COMMAND,
7563034Sdougm 					    dataset);
7573034Sdougm 			}
7583034Sdougm 			pfile = popen(command, "r");
7593034Sdougm 			if (pfile != NULL)
7603034Sdougm 			    ret = pclose(pfile);
7613034Sdougm 			switch (ret) {
7623034Sdougm 			default:
7633034Sdougm 			case 1:
7643034Sdougm 			    ret = SA_SYSTEM_ERR;
7653034Sdougm 			    break;
7663034Sdougm 			case 2:
7673034Sdougm 			    ret = SA_SYNTAX_ERR;
7683034Sdougm 			    break;
7693034Sdougm 			case 0:
7703034Sdougm 			    break;
7713034Sdougm 			}
7723034Sdougm 		    }
7733034Sdougm 		    if (optstring != NULL) {
7743034Sdougm 			free(optstring);
7753034Sdougm 		    }
7763034Sdougm 		    if (zfsopts != NULL)
7773034Sdougm 			free(zfsopts);
7783034Sdougm 		}
7793034Sdougm 		if (proto != NULL)
7803034Sdougm 		    sa_free_attr_string(proto);
7813034Sdougm 		if (dataset != NULL)
7823034Sdougm 		    free(dataset);
7833034Sdougm 	    }
7843034Sdougm 	    free(command);
7853034Sdougm 	}
7863034Sdougm 	return (ret);
7873034Sdougm }
7883034Sdougm 
7893034Sdougm /*
7903034Sdougm  * sa_group_is_zfs(group)
7913034Sdougm  *
7923034Sdougm  * Given the group, determine if the zfs attribute is set.
7933034Sdougm  */
7943034Sdougm 
7953034Sdougm int
7963034Sdougm sa_group_is_zfs(sa_group_t group)
7973034Sdougm {
7983034Sdougm 	char *zfs;
7993034Sdougm 	int ret = 0;
8003034Sdougm 
8013034Sdougm 	zfs = sa_get_group_attr(group, "zfs");
8023034Sdougm 	if (zfs != NULL) {
8033034Sdougm 	    ret = 1;
8043034Sdougm 	    sa_free_attr_string(zfs);
8053034Sdougm 	}
8063034Sdougm 	return (ret);
8073034Sdougm }
8083034Sdougm 
8093034Sdougm /*
8103034Sdougm  * sa_path_is_zfs(path)
8113034Sdougm  *
8123034Sdougm  * Check to see if the file system path represents is of type "zfs".
8133034Sdougm  */
8143034Sdougm 
8153034Sdougm int
8163034Sdougm sa_path_is_zfs(char *path)
8173034Sdougm {
8183034Sdougm 	char *fstype;
8193034Sdougm 	int ret = 0;
8203034Sdougm 
8213034Sdougm 	fstype = sa_fstype(path);
8223034Sdougm 	if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
8233034Sdougm 	    ret = 1;
8243034Sdougm 	}
8253034Sdougm 	if (fstype != NULL)
8263034Sdougm 	    sa_free_fstype(fstype);
8273034Sdougm 	return (ret);
8283034Sdougm }
829