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 /*
23*3407Sdougm  * 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 static libzfs_handle_t *zfs_libhandle = NULL;
543218Sdougm static zfs_handle_t **zfs_list = NULL;
553218Sdougm static size_t zfs_list_count = 0;
563218Sdougm 
573218Sdougm /*
583218Sdougm  * sa_zfs_init()
593218Sdougm  *
603218Sdougm  * initialize an access handle into libzfs
613218Sdougm  */
623218Sdougm 
633218Sdougm void
643218Sdougm sa_zfs_init()
653218Sdougm {
663218Sdougm 	zfs_libhandle = libzfs_init();
673218Sdougm 	libzfs_print_on_error(zfs_libhandle, B_TRUE);
683218Sdougm }
693218Sdougm 
703218Sdougm /*
713218Sdougm  * sa_zfs_fini()
723218Sdougm  *
733218Sdougm  * cleanup data structures and the libzfs handle used for accessing
743218Sdougm  * zfs file share info.
753218Sdougm  */
763218Sdougm 
773218Sdougm void
783218Sdougm sa_zfs_fini()
793218Sdougm {
803218Sdougm 	if (zfs_libhandle != NULL) {
813218Sdougm 	    libzfs_fini(zfs_libhandle);
823218Sdougm 	    zfs_libhandle = NULL;
833218Sdougm 	    if (zfs_list != NULL) {
843218Sdougm 		/*
853218Sdougm 		 * contents of zfs_list were already freed by the call to
863218Sdougm 		 * libzfs_fini().
873218Sdougm 		 */
883218Sdougm 		free(zfs_list);
893218Sdougm 		zfs_list = NULL;
903218Sdougm 	    }
913218Sdougm 	}
923218Sdougm }
933218Sdougm 
943218Sdougm /*
953218Sdougm  * get_one_filesystem(zfs_handle_t, data)
963218Sdougm  *
973218Sdougm  * an interator function called while iterating through the ZFS
983218Sdougm  * root. It accumulates into an array of file system handles that can
993218Sdougm  * be used to derive info about those file systems.
1003034Sdougm  */
1013034Sdougm 
1023218Sdougm static int
1033218Sdougm get_one_filesystem(zfs_handle_t *zhp, void *data)
1043218Sdougm {
1053218Sdougm 	get_all_cbdata_t *cbp = data;
1063218Sdougm 
1073218Sdougm 	/*
1083218Sdougm 	 * Skip any zvols
1093218Sdougm 	 */
1103218Sdougm 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
1113218Sdougm 		zfs_close(zhp);
1123218Sdougm 		return (0);
1133218Sdougm 	}
1143218Sdougm 
1153218Sdougm 	if (cbp->cb_alloc == cbp->cb_used) {
1163218Sdougm 		zfs_handle_t **handles;
1173218Sdougm 
1183218Sdougm 		if (cbp->cb_alloc == 0)
1193218Sdougm 			cbp->cb_alloc = 64;
1203218Sdougm 		else
1213218Sdougm 			cbp->cb_alloc *= 2;
1223218Sdougm 
1233218Sdougm 		handles = calloc(1, cbp->cb_alloc * sizeof (void *));
1243218Sdougm 		if (handles == NULL) {
1253218Sdougm 		    return (0);
1263218Sdougm 		}
1273218Sdougm 
1283218Sdougm 		if (cbp->cb_handles) {
1293218Sdougm 			(void) memcpy(handles, cbp->cb_handles,
1303218Sdougm 			    cbp->cb_used * sizeof (void *));
1313218Sdougm 			free(cbp->cb_handles);
1323218Sdougm 		}
1333218Sdougm 
1343218Sdougm 		cbp->cb_handles = handles;
1353218Sdougm 	}
1363218Sdougm 
1373218Sdougm 	cbp->cb_handles[cbp->cb_used++] = zhp;
1383218Sdougm 
1393218Sdougm 	return (zfs_iter_filesystems(zhp, get_one_filesystem, data));
1403218Sdougm }
1413218Sdougm 
1423218Sdougm /*
1433218Sdougm  * get_all_filesystems(zfs_handle_t ***fslist, size_t *count)
1443218Sdougm  *
1453218Sdougm  * iterate through all ZFS file systems starting at the root. Returns
1463218Sdougm  * a count and an array of handle pointers. Allocating is only done
1473218Sdougm  * once. The caller does not need to free since it will be done at
1483218Sdougm  * sa_zfs_fini() time.
1493218Sdougm  */
1503218Sdougm 
1513218Sdougm static void
1523218Sdougm get_all_filesystems(zfs_handle_t ***fslist, size_t *count)
1533218Sdougm {
1543218Sdougm 	get_all_cbdata_t cb = { 0 };
1553218Sdougm 
1563218Sdougm 	if (zfs_list != NULL) {
1573218Sdougm 	    *fslist = zfs_list;
1583218Sdougm 	    *count = zfs_list_count;
1593218Sdougm 	    return;
1603218Sdougm 	}
1613218Sdougm 
1623218Sdougm 	(void) zfs_iter_root(zfs_libhandle, get_one_filesystem, &cb);
1633218Sdougm 
1643218Sdougm 	zfs_list = *fslist = cb.cb_handles;
1653218Sdougm 	zfs_list_count = *count = cb.cb_used;
1663218Sdougm }
1673218Sdougm 
1683218Sdougm /*
1693218Sdougm  * mountpoint_compare(a, b)
1703218Sdougm  *
1713218Sdougm  * compares the mountpoint on two zfs file systems handles.
1723218Sdougm  * returns values following strcmp() model.
1733218Sdougm  */
1743218Sdougm 
1753218Sdougm static int
1763218Sdougm mountpoint_compare(const void *a, const void *b)
1773218Sdougm {
1783218Sdougm 	zfs_handle_t **za = (zfs_handle_t **)a;
1793218Sdougm 	zfs_handle_t **zb = (zfs_handle_t **)b;
1803218Sdougm 	char mounta[MAXPATHLEN];
1813218Sdougm 	char mountb[MAXPATHLEN];
1823218Sdougm 
1833218Sdougm 	verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
1843218Sdougm 	    sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
1853218Sdougm 	verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
1863218Sdougm 	    sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
1873218Sdougm 
1883218Sdougm 	return (strcmp(mounta, mountb));
1893218Sdougm }
1903218Sdougm 
1913034Sdougm /*
1923034Sdougm  * get_zfs_dataset(path)
1933034Sdougm  *
1943034Sdougm  * get the name of the ZFS dataset the path is equivalent to.  The
1953034Sdougm  * dataset name is used for get/set of ZFS properties since libzfs
1963034Sdougm  * requires a dataset to do a zfs_open().
1973034Sdougm  */
1983034Sdougm 
1993034Sdougm static char *
2003034Sdougm get_zfs_dataset(char *path)
2013034Sdougm {
2023218Sdougm 	size_t i, count = 0;
2033034Sdougm 	char *dataset = NULL;
2043218Sdougm 	zfs_handle_t **zlist;
2053218Sdougm 	char mountpoint[ZFS_MAXPROPLEN];
2063218Sdougm 
2073218Sdougm 	get_all_filesystems(&zlist, &count);
2083218Sdougm 	qsort(zlist, count, sizeof (void *), mountpoint_compare);
2093218Sdougm 	for (i = 0; i < count; i++) {
2103218Sdougm 	    /* must have a mountpoint */
2113218Sdougm 	    if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint,
2123218Sdougm 		sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
2133218Sdougm 		/* no mountpoint */
2143218Sdougm 		continue;
2153218Sdougm 	    }
2163034Sdougm 
2173218Sdougm 	    /* mountpoint must be a path */
2183218Sdougm 	    if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
2193218Sdougm 		strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0)
2203218Sdougm 		continue;
2213218Sdougm 
2223218Sdougm 	    /* canmount must be set */
2233218Sdougm 	    if (!zfs_prop_get_int(zlist[i], ZFS_PROP_CANMOUNT))
2243218Sdougm 		continue;
2253218Sdougm 
2263218Sdougm 	/*
2273218Sdougm 	 * have a mountable handle but want to skip those marked none
2283218Sdougm 	 * and legacy
2293218Sdougm 	 */
2303218Sdougm 	    if (strcmp(mountpoint, path) == 0) {
2313218Sdougm 		dataset = (char *)zfs_get_name(zlist[i]);
2323218Sdougm 		break;
2333034Sdougm 	    }
2343218Sdougm 
2353034Sdougm 	}
2363218Sdougm 
2373034Sdougm 	if (dataset != NULL) {
2383034Sdougm 	    dataset = strdup(dataset);
2393034Sdougm 	}
2403034Sdougm 	return (dataset);
2413034Sdougm }
2423034Sdougm 
2433034Sdougm /*
2443034Sdougm  * get_zfs_property(dataset, property)
2453034Sdougm  *
2463034Sdougm  * Get the file system property specified from the ZFS dataset.
2473034Sdougm  */
2483034Sdougm 
2493034Sdougm static char *
2503034Sdougm get_zfs_property(char *dataset, zfs_prop_t property)
2513034Sdougm {
2523034Sdougm 	zfs_handle_t *handle = NULL;
2533034Sdougm 	char shareopts[ZFS_MAXPROPLEN];
2543034Sdougm 	libzfs_handle_t *libhandle;
2553034Sdougm 
2563034Sdougm 	libhandle = libzfs_init();
2573034Sdougm 	if (libhandle != NULL) {
2583034Sdougm 	    handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
2593034Sdougm 	    if (handle != NULL) {
2603034Sdougm 		if (zfs_prop_get(handle, property, shareopts,
2613034Sdougm 				sizeof (shareopts), NULL, NULL, 0,
2623218Sdougm 				B_FALSE) == 0) {
2633034Sdougm 		    zfs_close(handle);
2643034Sdougm 		    libzfs_fini(libhandle);
2653034Sdougm 		    return (strdup(shareopts));
2663034Sdougm 		}
2673034Sdougm 		zfs_close(handle);
2683034Sdougm 	    }
2693034Sdougm 	    libzfs_fini(libhandle);
2703034Sdougm 	}
2713034Sdougm 	return (NULL);
2723034Sdougm }
2733034Sdougm 
2743034Sdougm /*
2753034Sdougm  * sa_zfs_is_shared(path)
2763034Sdougm  *
2773034Sdougm  * Check to see if the ZFS path provided has the sharenfs option set
2783034Sdougm  * or not.
2793034Sdougm  */
2803034Sdougm 
2813034Sdougm int
2823034Sdougm sa_zfs_is_shared(char *path)
2833034Sdougm {
2843034Sdougm 	int ret = 0;
2853034Sdougm 	char *dataset;
2863034Sdougm 	zfs_handle_t *handle = NULL;
2873034Sdougm 	char shareopts[ZFS_MAXPROPLEN];
2883034Sdougm 	libzfs_handle_t *libhandle;
2893034Sdougm 
2903034Sdougm 	dataset = get_zfs_dataset(path);
2913034Sdougm 	if (dataset != NULL) {
2923034Sdougm 	    libhandle = libzfs_init();
2933034Sdougm 	    if (libhandle != NULL) {
2943034Sdougm 		handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
2953034Sdougm 		if (handle != NULL) {
2963034Sdougm 		    if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts,
2973034Sdougm 					sizeof (shareopts), NULL, NULL, 0,
2983218Sdougm 					B_FALSE) == 0 &&
2993034Sdougm 			strcmp(shareopts, "off") != 0)
3003034Sdougm 			ret = 1; /* it is shared */
3013034Sdougm 		    zfs_close(handle);
3023034Sdougm 		}
3033034Sdougm 		libzfs_fini(libhandle);
3043034Sdougm 	    }
3053034Sdougm 	    free(dataset);
3063034Sdougm 	}
3073034Sdougm 	return (ret);
3083034Sdougm }
3093034Sdougm 
3103034Sdougm /*
3113034Sdougm  * find_or_create_group(groupname, proto, *err)
3123034Sdougm  *
3133034Sdougm  * While walking the ZFS tree, we need to add shares to a defined
3143034Sdougm  * group. If the group doesn't exist, create it first, making sure it
3153034Sdougm  * is marked as a ZFS group.
3163034Sdougm  *
3173108Sdougm  * Note that all ZFS shares are in a subgroup of the top level group
3183108Sdougm  * called "zfs".
3193034Sdougm  */
3203034Sdougm 
3213034Sdougm static sa_group_t
3223034Sdougm find_or_create_group(char *groupname, char *proto, int *err)
3233034Sdougm {
3243034Sdougm 	sa_group_t group;
3253034Sdougm 	sa_optionset_t optionset;
3263034Sdougm 	int ret = SA_OK;
3273034Sdougm 
3283034Sdougm 	/*
3293034Sdougm 	 * we check to see if the "zfs" group exists. Since this
3303034Sdougm 	 * should be the top level group, we don't want the
3313034Sdougm 	 * parent. This is to make sure the zfs group has been created
3323034Sdougm 	 * and to created if it hasn't been.
3333034Sdougm 	 */
3343034Sdougm 	group = sa_get_group(groupname);
3353034Sdougm 	if (group == NULL) {
3363034Sdougm 	    group = sa_create_group(groupname, &ret);
3373108Sdougm 
3383108Sdougm 	    /* make sure this is flagged as a ZFS group */
3393034Sdougm 	    if (group != NULL)
3403034Sdougm 		ret = sa_set_group_attr(group, "zfs", "true");
3413034Sdougm 	}
3423034Sdougm 	if (group != NULL) {
3433034Sdougm 	    if (proto != NULL) {
3443034Sdougm 		optionset = sa_get_optionset(group, proto);
3453034Sdougm 		if (optionset == NULL) {
3463034Sdougm 		    optionset = sa_create_optionset(group, proto);
3473034Sdougm 		} else {
3483034Sdougm 		    char **protolist;
3493034Sdougm 		    int numprotos, i;
3503034Sdougm 		    numprotos = sa_get_protocols(&protolist);
3513034Sdougm 		    for (i = 0; i < numprotos; i++) {
3523034Sdougm 			optionset = sa_create_optionset(group, protolist[i]);
3533034Sdougm 		    }
3543034Sdougm 		    if (protolist != NULL)
3553034Sdougm 			free(protolist);
3563034Sdougm 		}
3573034Sdougm 	    }
3583034Sdougm 	}
3593034Sdougm 	if (err != NULL)
3603034Sdougm 	    *err = ret;
3613034Sdougm 	return (group);
3623034Sdougm }
3633034Sdougm 
3643034Sdougm /*
3653108Sdougm  * find_or_create_zfs_subgroup(groupname, optstring, *err)
3663108Sdougm  *
3673108Sdougm  * ZFS shares will be in a subgroup of the "zfs" master group.  This
3683108Sdougm  * function looks to see if the groupname exists and returns it if it
3693108Sdougm  * does or else creates a new one with the specified name and returns
3703108Sdougm  * that.  The "zfs" group will exist before we get here, but we make
3713108Sdougm  * sure just in case.
3723108Sdougm  *
3733108Sdougm  * err must be a valid pointer.
3743108Sdougm  */
3753108Sdougm 
3763108Sdougm static sa_group_t
3773108Sdougm find_or_create_zfs_subgroup(char *groupname, char *optstring, int *err)
3783108Sdougm {
3793108Sdougm 	sa_group_t group = NULL;
3803108Sdougm 	sa_group_t zfs;
3813108Sdougm 	char *name;
3823108Sdougm 	char *options;
3833108Sdougm 
3843108Sdougm 	/* start with the top-level "zfs" group */
3853108Sdougm 	zfs = sa_get_group("zfs");
3863108Sdougm 	*err = SA_OK;
3873108Sdougm 	if (zfs != NULL) {
3883108Sdougm 	    for (group = sa_get_sub_group(zfs); group != NULL;
3893108Sdougm 		group = sa_get_next_group(group)) {
3903108Sdougm 		name = sa_get_group_attr(group, "name");
3913108Sdougm 		if (name != NULL && strcmp(name, groupname) == 0) {
3923108Sdougm 		    /* have the group so break out of here */
3933108Sdougm 		    sa_free_attr_string(name);
3943108Sdougm 		    break;
3953108Sdougm 		}
3963108Sdougm 		if (name != NULL)
3973108Sdougm 		    sa_free_attr_string(name);
3983108Sdougm 	    }
3993108Sdougm 
4003108Sdougm 	    if (group == NULL) {
4013108Sdougm 		/* need to create the sub-group since it doesn't exist */
4023108Sdougm 		group = _sa_create_zfs_group(zfs, groupname);
4033108Sdougm 		if (group != NULL) {
4043108Sdougm 		    set_node_attr(group, "zfs", "true");
4053108Sdougm 		}
4063108Sdougm 		if (strcmp(optstring, "on") == 0)
4073108Sdougm 		    optstring = "rw";
4083108Sdougm 		if (group != NULL) {
4093108Sdougm 		    options = strdup(optstring);
4103108Sdougm 		    if (options != NULL) {
4113108Sdougm 			*err = sa_parse_legacy_options(group, options, "nfs");
4123108Sdougm 			free(options);
4133108Sdougm 		    } else {
4143108Sdougm 			*err = SA_NO_MEMORY;
4153108Sdougm 		    }
4163108Sdougm 		}
4173108Sdougm 	    }
4183108Sdougm 	}
4193108Sdougm 	return (group);
4203108Sdougm }
4213108Sdougm 
4223108Sdougm /*
4233034Sdougm  * sa_get_zfs_shares(groupname)
4243034Sdougm  *
4253034Sdougm  * Walk the mnttab for all zfs mounts and determine which are
4263034Sdougm  * shared. Find or create the appropriate group/sub-group to contain
4273034Sdougm  * the shares.
4283034Sdougm  *
4293034Sdougm  * All shares are in a sub-group that will hold the properties. This
4303034Sdougm  * allows representing the inherited property model.
4313034Sdougm  */
4323034Sdougm 
4333034Sdougm int
4343034Sdougm sa_get_zfs_shares(char *groupname)
4353034Sdougm {
4363034Sdougm 	sa_group_t group;
4373034Sdougm 	sa_group_t zfsgroup;
4383034Sdougm 	int legacy = 0;
4393034Sdougm 	int err;
4403218Sdougm 	zfs_handle_t **zlist;
4413034Sdougm 	char shareopts[ZFS_MAXPROPLEN];
4423034Sdougm 	sa_share_t share;
4433034Sdougm 	zfs_source_t source;
4443034Sdougm 	char sourcestr[ZFS_MAXPROPLEN];
4453218Sdougm 	char mountpoint[ZFS_MAXPROPLEN];
4463108Sdougm 	char *options;
4473218Sdougm 	size_t count = 0, i;
4483034Sdougm 
4493034Sdougm 	/*
4503034Sdougm 	 * if we can't access libzfs, don't bother doing anything.
4513034Sdougm 	 */
4523218Sdougm 	if (zfs_libhandle == NULL)
4533034Sdougm 	    return (SA_SYSTEM_ERR);
4543034Sdougm 
4553034Sdougm 	zfsgroup = find_or_create_group(groupname, "nfs", &err);
4563034Sdougm 	if (zfsgroup != NULL) {
4573034Sdougm 		/*
4583034Sdougm 		 * need to walk the mounted ZFS pools and datasets to
4593034Sdougm 		 * find shares that are possible.
4603034Sdougm 		 */
4613218Sdougm 	    get_all_filesystems(&zlist, &count);
4623218Sdougm 	    qsort(zlist, count, sizeof (void *), mountpoint_compare);
4633218Sdougm 
4643034Sdougm 	    group = zfsgroup;
4653218Sdougm 	    for (i = 0; i < count; i++) {
4663218Sdougm 		char *dataset;
4673218Sdougm 
4683218Sdougm 		source = ZFS_SRC_ALL;
4693218Sdougm 		if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint,
4703218Sdougm 					sizeof (mountpoint), NULL, NULL, 0,
4713218Sdougm 					B_FALSE) != 0) {
4723218Sdougm 		    /* no mountpoint */
4733218Sdougm 		    continue;
4743218Sdougm 		}
4753218Sdougm 
4763218Sdougm 		/*
4773218Sdougm 		 * zfs_get_name value must not be freed. It is just a
4783218Sdougm 		 * pointer to a value in the handle.
4793218Sdougm 		 */
4803218Sdougm 		if ((dataset = (char *)zfs_get_name(zlist[i])) == NULL)
4813218Sdougm 		    continue;
4823218Sdougm 
4833218Sdougm 		/*
4843218Sdougm 		 * only deal with "mounted" file systems since
4853218Sdougm 		 * unmounted file systems can't actually be shared.
4863218Sdougm 		 */
4873218Sdougm 
4883218Sdougm 		if (!zfs_is_mounted(zlist[i], NULL))
4893218Sdougm 		    continue;
4903218Sdougm 
4913218Sdougm 		if (zfs_prop_get(zlist[i], ZFS_PROP_SHARENFS, shareopts,
4923034Sdougm 					sizeof (shareopts), &source, sourcestr,
4933034Sdougm 					ZFS_MAXPROPLEN,
4943218Sdougm 					B_FALSE) == 0 &&
4953034Sdougm 			strcmp(shareopts, "off") != 0) {
4963218Sdougm 		    /* it is shared so add to list */
4973218Sdougm 		    share = sa_find_share(mountpoint);
4983218Sdougm 		    err = SA_OK;
4993218Sdougm 		    if (share != NULL) {
5003218Sdougm 			/*
5013218Sdougm 			 * A zfs file system had been shared
5023218Sdougm 			 * through traditional methods
5033218Sdougm 			 * (share/dfstab or added to a non-zfs
5043218Sdougm 			 * group.  Now it has been added to a
5053218Sdougm 			 * ZFS group via the zfs
5063218Sdougm 			 * command. Remove from previous
5073218Sdougm 			 * config and setup with current
5083218Sdougm 			 * options.
5093218Sdougm 			 */
5103218Sdougm 			err = sa_remove_share(share);
5113218Sdougm 			share = NULL;
5123218Sdougm 		    }
5133218Sdougm 		    if (err == SA_OK) {
5143218Sdougm 			if (source & ZFS_SRC_INHERITED) {
5153218Sdougm 			    int doshopt = 0;
5163218Sdougm 			/*
5173218Sdougm 			 * Need to find the "real" parent
5183218Sdougm 			 * sub-group. It may not be mounted, but it
5193218Sdougm 			 * was identified in the "sourcestr"
5203218Sdougm 			 * variable. The real parent not mounted can
5213218Sdougm 			 * occur if "canmount=off and sharenfs=on".
5223218Sdougm 			 */
5233218Sdougm 			    group = find_or_create_zfs_subgroup(sourcestr,
5243218Sdougm 							shareopts, &doshopt);
5253218Sdougm 			    if (group != NULL) {
5263218Sdougm 				share = _sa_add_share(group, mountpoint,
5273034Sdougm 							SA_SHARE_TRANSIENT,
5283034Sdougm 							&err);
5293218Sdougm 				/*
5303218Sdougm 				 * some options may only be on
5313218Sdougm 				 * shares. If the opt string
5323218Sdougm 				 * contains one of those, we
5333218Sdougm 				 * put it just on the share.
5343218Sdougm 				 */
5353218Sdougm 				if (share != NULL &&
5363218Sdougm 				    doshopt == SA_PROP_SHARE_ONLY) {
5373218Sdougm 				    options = strdup(shareopts);
5383218Sdougm 				    if (options != NULL) {
5393218Sdougm 					err = sa_parse_legacy_options(share,
5403108Sdougm 								options, "nfs");
5413218Sdougm 					free(options);
5423108Sdougm 				    }
5433108Sdougm 				}
5443034Sdougm 			    } else {
5453218Sdougm 				err = SA_NO_MEMORY;
5463218Sdougm 			    }
5473218Sdougm 			} else {
5483218Sdougm 			    group = _sa_create_zfs_group(zfsgroup, dataset);
5493218Sdougm 			    if (group == NULL) {
5503218Sdougm 				static int err = 0;
5513108Sdougm 				/*
5523218Sdougm 				 * there is a problem, but we can't do
5533218Sdougm 				 * anything about it at this point so
5543218Sdougm 				 * we issue a warning an move on.
5553108Sdougm 				 */
5563218Sdougm 				if (err == 0) {
5573218Sdougm 				    /* only print error once */
5583218Sdougm 				    (void) fprintf(stderr,
559*3407Sdougm 					dgettext(TEXT_DOMAIN,
560*3407Sdougm 						"Cannot create ZFS subgroup "
5613218Sdougm 						"during initialization:"
5623218Sdougm 						" %s\n"),
5633218Sdougm 					sa_errorstr(SA_SYSTEM_ERR));
5643218Sdougm 				    err = 1;
5653218Sdougm 				}
5663218Sdougm 				continue;
5673218Sdougm 			    }
5683218Sdougm 			    set_node_attr(group, "zfs", "true");
5693218Sdougm 			    share = _sa_add_share(group, mountpoint,
5703218Sdougm 						SA_SHARE_TRANSIENT, &err);
5713218Sdougm 			    if (err == SA_OK) {
5723218Sdougm 				if (strcmp(shareopts, "on") != 0) {
5733218Sdougm 				    options = strdup(shareopts);
5743218Sdougm 				    if (options != NULL) {
5753218Sdougm 					err = sa_parse_legacy_options(group,
5763034Sdougm 									options,
5773034Sdougm 									"nfs");
5783218Sdougm 					free(options);
5793218Sdougm 				    }
5803218Sdougm 				    if (err == SA_PROP_SHARE_ONLY) {
5813108Sdougm 					/*
5823108Sdougm 					 * Same as above, some
5833108Sdougm 					 * properties may only be on
5843108Sdougm 					 * shares, but due to the ZFS
5853108Sdougm 					 * sub-groups being
5863108Sdougm 					 * artificial, we sometimes
5873108Sdougm 					 * get this and have to deal
5883108Sdougm 					 * with it. We do it by
5893108Sdougm 					 * attempting to put it on the
5903108Sdougm 					 * share.
5913108Sdougm 					 */
5923218Sdougm 					options = strdup(shareopts);
5933218Sdougm 					if (options != NULL)
5943218Sdougm 					    err = sa_parse_legacy_options(
5953108Sdougm 									share,
5963108Sdougm 									options,
5973108Sdougm 									"nfs");
5983108Sdougm 					    free(options);
5993108Sdougm 					}
6003218Sdougm 				    /* unmark the share's changed state */
6013218Sdougm 				    set_node_attr(share, "changed", NULL);
6023034Sdougm 				}
6033034Sdougm 			    }
6043034Sdougm 			}
6053034Sdougm 		    }
6063034Sdougm 		}
6073034Sdougm 	    }
6083034Sdougm 	}
6093218Sdougm 	/*
6103218Sdougm 	 * Don't need to free the "zlist" variable since it is only a
6113218Sdougm 	 * pointer to a cached value that will be freed when
6123218Sdougm 	 * sa_fini() is called.
6133218Sdougm 	 */
6143034Sdougm 	return (legacy);
6153034Sdougm }
6163034Sdougm 
6173034Sdougm #define	COMMAND		"/usr/sbin/zfs"
6183034Sdougm 
6193034Sdougm /*
6203034Sdougm  * sa_zfs_set_sharenfs(group, path, on)
6213034Sdougm  *
6223034Sdougm  * Update the "sharenfs" property on the path. If on is true, then set
6233034Sdougm  * to the properties on the group or "on" if no properties are
6243034Sdougm  * defined. Set to "off" if on is false.
6253034Sdougm  */
6263034Sdougm 
6273034Sdougm int
6283034Sdougm sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
6293034Sdougm {
6303034Sdougm 	int ret = SA_NOT_IMPLEMENTED;
6313034Sdougm 	char *command;
6323034Sdougm 
6333034Sdougm 	command = malloc(ZFS_MAXPROPLEN * 2);
6343034Sdougm 	if (command != NULL) {
6353034Sdougm 	    char *opts = NULL;
6363034Sdougm 	    char *dataset;
6373034Sdougm 	    FILE *pfile;
6383034Sdougm 	    /* for now, NFS is always available for "zfs" */
6393034Sdougm 	    if (on) {
6403034Sdougm 		opts = sa_proto_legacy_format("nfs", group, 1);
6413034Sdougm 		if (opts != NULL && strlen(opts) == 0) {
6423034Sdougm 		    free(opts);
6433034Sdougm 		    opts = strdup("on");
6443034Sdougm 		}
6453034Sdougm 	    }
6463034Sdougm 	    dataset = get_zfs_dataset(path);
6473034Sdougm 	    if (dataset != NULL) {
6483034Sdougm 		(void) snprintf(command, ZFS_MAXPROPLEN * 2,
6493034Sdougm 				"%s set sharenfs=\"%s\" %s", COMMAND,
6503034Sdougm 				opts != NULL ? opts : "off",
6513034Sdougm 				dataset);
6523034Sdougm 		pfile = popen(command, "r");
6533034Sdougm 		if (pfile != NULL) {
6543034Sdougm 		    ret = pclose(pfile);
6553034Sdougm 		    if (ret != 0)
6563034Sdougm 			ret = SA_SYSTEM_ERR;
6573034Sdougm 		}
6583034Sdougm 	    }
6593034Sdougm 	    if (opts != NULL)
6603034Sdougm 		free(opts);
6613034Sdougm 	    if (dataset != NULL)
6623034Sdougm 		free(dataset);
6633034Sdougm 	    free(command);
6643034Sdougm 	}
6653034Sdougm 	return (ret);
6663034Sdougm }
6673034Sdougm 
6683034Sdougm /*
6693034Sdougm  * sa_zfs_update(group)
6703034Sdougm  *
6713034Sdougm  * call back to ZFS to update the share if necessary.
6723034Sdougm  * Don't do it if it isn't a real change.
6733034Sdougm  */
6743034Sdougm int
6753034Sdougm sa_zfs_update(sa_group_t group)
6763034Sdougm {
6773034Sdougm 	sa_optionset_t protopt;
6783034Sdougm 	sa_group_t parent;
6793034Sdougm 	char *command;
6803034Sdougm 	char *optstring;
6813034Sdougm 	int ret = SA_OK;
6823034Sdougm 	int doupdate = 0;
6833034Sdougm 	FILE *pfile;
6843034Sdougm 
6853034Sdougm 	if (sa_is_share(group))
6863034Sdougm 	    parent = sa_get_parent_group(group);
6873034Sdougm 	else
6883034Sdougm 	    parent = group;
6893034Sdougm 
6903034Sdougm 	if (parent != NULL) {
6913034Sdougm 	    command = malloc(ZFS_MAXPROPLEN * 2);
6923034Sdougm 	    if (command == NULL)
6933034Sdougm 		return (SA_NO_MEMORY);
6943034Sdougm 
6953034Sdougm 	    *command = '\0';
6963034Sdougm 	    for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
6973034Sdougm 		protopt = sa_get_next_optionset(protopt)) {
6983034Sdougm 
6993034Sdougm 		char *proto = sa_get_optionset_attr(protopt, "type");
7003034Sdougm 		char *path;
7013034Sdougm 		char *dataset = NULL;
7023034Sdougm 		char *zfsopts = NULL;
7033034Sdougm 
7043034Sdougm 		if (sa_is_share(group)) {
7053034Sdougm 		    path = sa_get_share_attr((sa_share_t)group, "path");
7063034Sdougm 		    if (path != NULL) {
7073034Sdougm 			dataset = get_zfs_dataset(path);
7083034Sdougm 			sa_free_attr_string(path);
7093034Sdougm 		    }
7103034Sdougm 		} else {
7113034Sdougm 		    dataset = sa_get_group_attr(group, "name");
7123034Sdougm 		}
7133034Sdougm 		/* update only when there is an optstring found */
7143034Sdougm 		doupdate = 0;
7153034Sdougm 		if (proto != NULL && dataset != NULL) {
7163034Sdougm 		    optstring = sa_proto_legacy_format(proto, group, 1);
7173034Sdougm 		    zfsopts = get_zfs_property(dataset, ZFS_PROP_SHARENFS);
7183034Sdougm 
7193034Sdougm 		    if (optstring != NULL && zfsopts != NULL) {
7203034Sdougm 			if (strcmp(optstring, zfsopts) != 0)
7213034Sdougm 			    doupdate++;
7223034Sdougm 		    }
7233034Sdougm 
7243034Sdougm 		    if (doupdate) {
7253034Sdougm 			if (optstring != NULL && strlen(optstring) > 0) {
7263034Sdougm 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
7273034Sdougm 					    "%s set sharenfs=%s %s", COMMAND,
7283034Sdougm 					    optstring, dataset);
7293034Sdougm 			} else {
7303034Sdougm 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
7313034Sdougm 					    "%s set sharenfs=on %s", COMMAND,
7323034Sdougm 					    dataset);
7333034Sdougm 			}
7343034Sdougm 			pfile = popen(command, "r");
7353034Sdougm 			if (pfile != NULL)
7363034Sdougm 			    ret = pclose(pfile);
7373034Sdougm 			switch (ret) {
7383034Sdougm 			default:
7393034Sdougm 			case 1:
7403034Sdougm 			    ret = SA_SYSTEM_ERR;
7413034Sdougm 			    break;
7423034Sdougm 			case 2:
7433034Sdougm 			    ret = SA_SYNTAX_ERR;
7443034Sdougm 			    break;
7453034Sdougm 			case 0:
7463034Sdougm 			    break;
7473034Sdougm 			}
7483034Sdougm 		    }
7493034Sdougm 		    if (optstring != NULL) {
7503034Sdougm 			free(optstring);
7513034Sdougm 		    }
7523034Sdougm 		    if (zfsopts != NULL)
7533034Sdougm 			free(zfsopts);
7543034Sdougm 		}
7553034Sdougm 		if (proto != NULL)
7563034Sdougm 		    sa_free_attr_string(proto);
7573034Sdougm 		if (dataset != NULL)
7583034Sdougm 		    free(dataset);
7593034Sdougm 	    }
7603034Sdougm 	    free(command);
7613034Sdougm 	}
7623034Sdougm 	return (ret);
7633034Sdougm }
7643034Sdougm 
7653034Sdougm /*
7663034Sdougm  * sa_group_is_zfs(group)
7673034Sdougm  *
7683034Sdougm  * Given the group, determine if the zfs attribute is set.
7693034Sdougm  */
7703034Sdougm 
7713034Sdougm int
7723034Sdougm sa_group_is_zfs(sa_group_t group)
7733034Sdougm {
7743034Sdougm 	char *zfs;
7753034Sdougm 	int ret = 0;
7763034Sdougm 
7773034Sdougm 	zfs = sa_get_group_attr(group, "zfs");
7783034Sdougm 	if (zfs != NULL) {
7793034Sdougm 	    ret = 1;
7803034Sdougm 	    sa_free_attr_string(zfs);
7813034Sdougm 	}
7823034Sdougm 	return (ret);
7833034Sdougm }
7843034Sdougm 
7853034Sdougm /*
7863034Sdougm  * sa_path_is_zfs(path)
7873034Sdougm  *
7883034Sdougm  * Check to see if the file system path represents is of type "zfs".
7893034Sdougm  */
7903034Sdougm 
7913034Sdougm int
7923034Sdougm sa_path_is_zfs(char *path)
7933034Sdougm {
7943034Sdougm 	char *fstype;
7953034Sdougm 	int ret = 0;
7963034Sdougm 
7973034Sdougm 	fstype = sa_fstype(path);
7983034Sdougm 	if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
7993034Sdougm 	    ret = 1;
8003034Sdougm 	}
8013034Sdougm 	if (fstype != NULL)
8023034Sdougm 	    sa_free_fstype(fstype);
8033034Sdougm 	return (ret);
8043034Sdougm }
805