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 /*
54*3910Sdougm  * sa_zfs_init(impl_handle)
553218Sdougm  *
56*3910Sdougm  * Initialize an access handle into libzfs.  The handle needs to stay
57*3910Sdougm  * around until sa_zfs_fini() in order to maintain the cache of
58*3910Sdougm  * mounts.
593218Sdougm  */
603218Sdougm 
613218Sdougm void
62*3910Sdougm sa_zfs_init(sa_handle_impl_t impl_handle)
633218Sdougm {
64*3910Sdougm 	impl_handle->zfs_libhandle = libzfs_init();
65*3910Sdougm 	libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE);
663218Sdougm }
673218Sdougm 
683218Sdougm /*
69*3910Sdougm  * 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
76*3910Sdougm sa_zfs_fini(sa_handle_impl_t impl_handle)
773218Sdougm {
78*3910Sdougm 	if (impl_handle->zfs_libhandle != NULL) {
79*3910Sdougm 	    libzfs_fini(impl_handle->zfs_libhandle);
80*3910Sdougm 	    impl_handle->zfs_libhandle = NULL;
81*3910Sdougm 	    if (impl_handle->zfs_list != NULL) {
823218Sdougm 		/*
833218Sdougm 		 * contents of zfs_list were already freed by the call to
843218Sdougm 		 * libzfs_fini().
853218Sdougm 		 */
86*3910Sdougm 		free(impl_handle->zfs_list);
87*3910Sdougm 		impl_handle->zfs_list = NULL;
88*3910Sdougm 		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
151*3910Sdougm get_all_filesystems(sa_handle_impl_t impl_handle,
152*3910Sdougm 			zfs_handle_t ***fslist, size_t *count)
1533218Sdougm {
1543218Sdougm 	get_all_cbdata_t cb = { 0 };
1553218Sdougm 
156*3910Sdougm 	if (impl_handle->zfs_list != NULL) {
157*3910Sdougm 	    *fslist = impl_handle->zfs_list;
158*3910Sdougm 	    *count = impl_handle->zfs_list_count;
1593218Sdougm 	    return;
1603218Sdougm 	}
1613218Sdougm 
162*3910Sdougm 	(void) zfs_iter_root(impl_handle->zfs_libhandle,
163*3910Sdougm 				get_one_filesystem, &cb);
1643218Sdougm 
165*3910Sdougm 	impl_handle->zfs_list = *fslist = cb.cb_handles;
166*3910Sdougm 	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 /*
193*3910Sdougm  * 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 *
201*3910Sdougm 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];
2073218Sdougm 
208*3910Sdougm 	get_all_filesystems(impl_handle, &zlist, &count);
2093218Sdougm 	qsort(zlist, count, sizeof (void *), mountpoint_compare);
2103218Sdougm 	for (i = 0; i < count; i++) {
2113218Sdougm 	    /* must have a mountpoint */
2123218Sdougm 	    if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint,
2133218Sdougm 		sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
2143218Sdougm 		/* no mountpoint */
2153218Sdougm 		continue;
2163218Sdougm 	    }
2173034Sdougm 
2183218Sdougm 	    /* mountpoint must be a path */
2193218Sdougm 	    if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
2203218Sdougm 		strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0)
2213218Sdougm 		continue;
2223218Sdougm 
2233218Sdougm 	    /* canmount must be set */
2243218Sdougm 	    if (!zfs_prop_get_int(zlist[i], ZFS_PROP_CANMOUNT))
2253218Sdougm 		continue;
2263218Sdougm 
2273218Sdougm 	/*
2283218Sdougm 	 * have a mountable handle but want to skip those marked none
2293218Sdougm 	 * and legacy
2303218Sdougm 	 */
2313218Sdougm 	    if (strcmp(mountpoint, path) == 0) {
2323218Sdougm 		dataset = (char *)zfs_get_name(zlist[i]);
2333218Sdougm 		break;
2343034Sdougm 	    }
2353218Sdougm 
2363034Sdougm 	}
2373218Sdougm 
2383034Sdougm 	if (dataset != NULL) {
2393034Sdougm 	    dataset = strdup(dataset);
2403034Sdougm 	}
2413034Sdougm 	return (dataset);
2423034Sdougm }
2433034Sdougm 
2443034Sdougm /*
2453034Sdougm  * get_zfs_property(dataset, property)
2463034Sdougm  *
2473034Sdougm  * Get the file system property specified from the ZFS dataset.
2483034Sdougm  */
2493034Sdougm 
2503034Sdougm static char *
2513034Sdougm get_zfs_property(char *dataset, zfs_prop_t property)
2523034Sdougm {
2533034Sdougm 	zfs_handle_t *handle = NULL;
2543034Sdougm 	char shareopts[ZFS_MAXPROPLEN];
2553034Sdougm 	libzfs_handle_t *libhandle;
2563034Sdougm 
2573034Sdougm 	libhandle = libzfs_init();
2583034Sdougm 	if (libhandle != NULL) {
2593034Sdougm 	    handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
2603034Sdougm 	    if (handle != NULL) {
2613034Sdougm 		if (zfs_prop_get(handle, property, shareopts,
2623034Sdougm 				sizeof (shareopts), NULL, NULL, 0,
2633218Sdougm 				B_FALSE) == 0) {
2643034Sdougm 		    zfs_close(handle);
2653034Sdougm 		    libzfs_fini(libhandle);
2663034Sdougm 		    return (strdup(shareopts));
2673034Sdougm 		}
2683034Sdougm 		zfs_close(handle);
2693034Sdougm 	    }
2703034Sdougm 	    libzfs_fini(libhandle);
2713034Sdougm 	}
2723034Sdougm 	return (NULL);
2733034Sdougm }
2743034Sdougm 
2753034Sdougm /*
276*3910Sdougm  * sa_zfs_is_shared(handle, path)
2773034Sdougm  *
2783034Sdougm  * Check to see if the ZFS path provided has the sharenfs option set
2793034Sdougm  * or not.
2803034Sdougm  */
2813034Sdougm 
2823034Sdougm int
283*3910Sdougm sa_zfs_is_shared(sa_handle_t sahandle, char *path)
2843034Sdougm {
2853034Sdougm 	int ret = 0;
2863034Sdougm 	char *dataset;
2873034Sdougm 	zfs_handle_t *handle = NULL;
2883034Sdougm 	char shareopts[ZFS_MAXPROPLEN];
2893034Sdougm 	libzfs_handle_t *libhandle;
2903034Sdougm 
291*3910Sdougm 	dataset = get_zfs_dataset((sa_handle_t)sahandle, path);
2923034Sdougm 	if (dataset != NULL) {
2933034Sdougm 	    libhandle = libzfs_init();
2943034Sdougm 	    if (libhandle != NULL) {
2953034Sdougm 		handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
2963034Sdougm 		if (handle != NULL) {
2973034Sdougm 		    if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts,
2983034Sdougm 					sizeof (shareopts), NULL, NULL, 0,
2993218Sdougm 					B_FALSE) == 0 &&
3003034Sdougm 			strcmp(shareopts, "off") != 0)
3013034Sdougm 			ret = 1; /* it is shared */
3023034Sdougm 		    zfs_close(handle);
3033034Sdougm 		}
3043034Sdougm 		libzfs_fini(libhandle);
3053034Sdougm 	    }
3063034Sdougm 	    free(dataset);
3073034Sdougm 	}
3083034Sdougm 	return (ret);
3093034Sdougm }
3103034Sdougm 
3113034Sdougm /*
3123034Sdougm  * find_or_create_group(groupname, proto, *err)
3133034Sdougm  *
3143034Sdougm  * While walking the ZFS tree, we need to add shares to a defined
3153034Sdougm  * group. If the group doesn't exist, create it first, making sure it
3163034Sdougm  * is marked as a ZFS group.
3173034Sdougm  *
3183108Sdougm  * Note that all ZFS shares are in a subgroup of the top level group
3193108Sdougm  * called "zfs".
3203034Sdougm  */
3213034Sdougm 
3223034Sdougm static sa_group_t
323*3910Sdougm find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err)
3243034Sdougm {
3253034Sdougm 	sa_group_t group;
3263034Sdougm 	sa_optionset_t optionset;
3273034Sdougm 	int ret = SA_OK;
3283034Sdougm 
3293034Sdougm 	/*
3303034Sdougm 	 * we check to see if the "zfs" group exists. Since this
3313034Sdougm 	 * should be the top level group, we don't want the
3323034Sdougm 	 * parent. This is to make sure the zfs group has been created
3333034Sdougm 	 * and to created if it hasn't been.
3343034Sdougm 	 */
335*3910Sdougm 	group = sa_get_group(handle, groupname);
3363034Sdougm 	if (group == NULL) {
337*3910Sdougm 	    group = sa_create_group(handle, groupname, &ret);
3383108Sdougm 
3393108Sdougm 	    /* make sure this is flagged as a ZFS group */
3403034Sdougm 	    if (group != NULL)
3413034Sdougm 		ret = sa_set_group_attr(group, "zfs", "true");
3423034Sdougm 	}
3433034Sdougm 	if (group != NULL) {
3443034Sdougm 	    if (proto != NULL) {
3453034Sdougm 		optionset = sa_get_optionset(group, proto);
3463034Sdougm 		if (optionset == NULL) {
3473034Sdougm 		    optionset = sa_create_optionset(group, proto);
3483034Sdougm 		} else {
3493034Sdougm 		    char **protolist;
3503034Sdougm 		    int numprotos, i;
3513034Sdougm 		    numprotos = sa_get_protocols(&protolist);
3523034Sdougm 		    for (i = 0; i < numprotos; i++) {
3533034Sdougm 			optionset = sa_create_optionset(group, protolist[i]);
3543034Sdougm 		    }
3553034Sdougm 		    if (protolist != NULL)
3563034Sdougm 			free(protolist);
3573034Sdougm 		}
3583034Sdougm 	    }
3593034Sdougm 	}
3603034Sdougm 	if (err != NULL)
3613034Sdougm 	    *err = ret;
3623034Sdougm 	return (group);
3633034Sdougm }
3643034Sdougm 
3653034Sdougm /*
3663108Sdougm  * find_or_create_zfs_subgroup(groupname, optstring, *err)
3673108Sdougm  *
3683108Sdougm  * ZFS shares will be in a subgroup of the "zfs" master group.  This
3693108Sdougm  * function looks to see if the groupname exists and returns it if it
3703108Sdougm  * does or else creates a new one with the specified name and returns
3713108Sdougm  * that.  The "zfs" group will exist before we get here, but we make
3723108Sdougm  * sure just in case.
3733108Sdougm  *
3743108Sdougm  * err must be a valid pointer.
3753108Sdougm  */
3763108Sdougm 
3773108Sdougm static sa_group_t
378*3910Sdougm find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname,
379*3910Sdougm 				char *optstring, int *err)
3803108Sdougm {
3813108Sdougm 	sa_group_t group = NULL;
3823108Sdougm 	sa_group_t zfs;
3833108Sdougm 	char *name;
3843108Sdougm 	char *options;
3853108Sdougm 
3863108Sdougm 	/* start with the top-level "zfs" group */
387*3910Sdougm 	zfs = sa_get_group(handle, "zfs");
3883108Sdougm 	*err = SA_OK;
3893108Sdougm 	if (zfs != NULL) {
3903108Sdougm 	    for (group = sa_get_sub_group(zfs); group != NULL;
3913108Sdougm 		group = sa_get_next_group(group)) {
3923108Sdougm 		name = sa_get_group_attr(group, "name");
3933108Sdougm 		if (name != NULL && strcmp(name, groupname) == 0) {
3943108Sdougm 		    /* have the group so break out of here */
3953108Sdougm 		    sa_free_attr_string(name);
3963108Sdougm 		    break;
3973108Sdougm 		}
3983108Sdougm 		if (name != NULL)
3993108Sdougm 		    sa_free_attr_string(name);
4003108Sdougm 	    }
4013108Sdougm 
4023108Sdougm 	    if (group == NULL) {
4033108Sdougm 		/* need to create the sub-group since it doesn't exist */
4043108Sdougm 		group = _sa_create_zfs_group(zfs, groupname);
4053108Sdougm 		if (group != NULL) {
4063108Sdougm 		    set_node_attr(group, "zfs", "true");
4073108Sdougm 		}
4083108Sdougm 		if (strcmp(optstring, "on") == 0)
4093108Sdougm 		    optstring = "rw";
4103108Sdougm 		if (group != NULL) {
4113108Sdougm 		    options = strdup(optstring);
4123108Sdougm 		    if (options != NULL) {
4133108Sdougm 			*err = sa_parse_legacy_options(group, options, "nfs");
4143108Sdougm 			free(options);
4153108Sdougm 		    } else {
4163108Sdougm 			*err = SA_NO_MEMORY;
4173108Sdougm 		    }
4183108Sdougm 		}
4193108Sdougm 	    }
4203108Sdougm 	}
4213108Sdougm 	return (group);
4223108Sdougm }
4233108Sdougm 
4243108Sdougm /*
425*3910Sdougm  * sa_get_zfs_shares(handle, groupname)
4263034Sdougm  *
4273034Sdougm  * Walk the mnttab for all zfs mounts and determine which are
4283034Sdougm  * shared. Find or create the appropriate group/sub-group to contain
4293034Sdougm  * the shares.
4303034Sdougm  *
4313034Sdougm  * All shares are in a sub-group that will hold the properties. This
4323034Sdougm  * allows representing the inherited property model.
4333034Sdougm  */
4343034Sdougm 
4353034Sdougm int
436*3910Sdougm sa_get_zfs_shares(sa_handle_t handle, char *groupname)
4373034Sdougm {
4383034Sdougm 	sa_group_t group;
4393034Sdougm 	sa_group_t zfsgroup;
4403034Sdougm 	int legacy = 0;
4413034Sdougm 	int err;
4423218Sdougm 	zfs_handle_t **zlist;
4433034Sdougm 	char shareopts[ZFS_MAXPROPLEN];
4443034Sdougm 	sa_share_t share;
4453034Sdougm 	zfs_source_t source;
4463034Sdougm 	char sourcestr[ZFS_MAXPROPLEN];
4473218Sdougm 	char mountpoint[ZFS_MAXPROPLEN];
4483108Sdougm 	char *options;
4493218Sdougm 	size_t count = 0, i;
450*3910Sdougm 	libzfs_handle_t *zfs_libhandle;
4513034Sdougm 
4523034Sdougm 	/*
453*3910Sdougm 	 * If we can't access libzfs, don't bother doing anything.
4543034Sdougm 	 */
455*3910Sdougm 	zfs_libhandle = ((sa_handle_impl_t)handle)->zfs_libhandle;
4563218Sdougm 	if (zfs_libhandle == NULL)
4573034Sdougm 	    return (SA_SYSTEM_ERR);
4583034Sdougm 
459*3910Sdougm 	zfsgroup = find_or_create_group(handle, groupname, "nfs", &err);
4603034Sdougm 	if (zfsgroup != NULL) {
4613034Sdougm 		/*
4623034Sdougm 		 * need to walk the mounted ZFS pools and datasets to
4633034Sdougm 		 * find shares that are possible.
4643034Sdougm 		 */
465*3910Sdougm 	    get_all_filesystems((sa_handle_impl_t)handle, &zlist, &count);
4663218Sdougm 	    qsort(zlist, count, sizeof (void *), mountpoint_compare);
4673218Sdougm 
4683034Sdougm 	    group = zfsgroup;
4693218Sdougm 	    for (i = 0; i < count; i++) {
4703218Sdougm 		char *dataset;
4713218Sdougm 
4723218Sdougm 		source = ZFS_SRC_ALL;
4733218Sdougm 		if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint,
4743218Sdougm 					sizeof (mountpoint), NULL, NULL, 0,
4753218Sdougm 					B_FALSE) != 0) {
4763218Sdougm 		    /* no mountpoint */
4773218Sdougm 		    continue;
4783218Sdougm 		}
4793218Sdougm 
4803218Sdougm 		/*
4813218Sdougm 		 * zfs_get_name value must not be freed. It is just a
4823218Sdougm 		 * pointer to a value in the handle.
4833218Sdougm 		 */
4843218Sdougm 		if ((dataset = (char *)zfs_get_name(zlist[i])) == NULL)
4853218Sdougm 		    continue;
4863218Sdougm 
4873218Sdougm 		/*
4883218Sdougm 		 * only deal with "mounted" file systems since
4893218Sdougm 		 * unmounted file systems can't actually be shared.
4903218Sdougm 		 */
4913218Sdougm 
4923218Sdougm 		if (!zfs_is_mounted(zlist[i], NULL))
4933218Sdougm 		    continue;
4943218Sdougm 
4953218Sdougm 		if (zfs_prop_get(zlist[i], ZFS_PROP_SHARENFS, shareopts,
4963034Sdougm 					sizeof (shareopts), &source, sourcestr,
4973034Sdougm 					ZFS_MAXPROPLEN,
4983218Sdougm 					B_FALSE) == 0 &&
4993034Sdougm 			strcmp(shareopts, "off") != 0) {
5003218Sdougm 		    /* it is shared so add to list */
501*3910Sdougm 		    share = sa_find_share(handle, mountpoint);
5023218Sdougm 		    err = SA_OK;
5033218Sdougm 		    if (share != NULL) {
5043218Sdougm 			/*
5053218Sdougm 			 * A zfs file system had been shared
5063218Sdougm 			 * through traditional methods
5073218Sdougm 			 * (share/dfstab or added to a non-zfs
5083218Sdougm 			 * group.  Now it has been added to a
5093218Sdougm 			 * ZFS group via the zfs
5103218Sdougm 			 * command. Remove from previous
5113218Sdougm 			 * config and setup with current
5123218Sdougm 			 * options.
5133218Sdougm 			 */
5143218Sdougm 			err = sa_remove_share(share);
5153218Sdougm 			share = NULL;
5163218Sdougm 		    }
5173218Sdougm 		    if (err == SA_OK) {
5183218Sdougm 			if (source & ZFS_SRC_INHERITED) {
5193218Sdougm 			    int doshopt = 0;
5203218Sdougm 			/*
5213218Sdougm 			 * Need to find the "real" parent
5223218Sdougm 			 * sub-group. It may not be mounted, but it
5233218Sdougm 			 * was identified in the "sourcestr"
5243218Sdougm 			 * variable. The real parent not mounted can
5253218Sdougm 			 * occur if "canmount=off and sharenfs=on".
5263218Sdougm 			 */
527*3910Sdougm 			    group = find_or_create_zfs_subgroup(handle,
528*3910Sdougm 							sourcestr,
5293218Sdougm 							shareopts, &doshopt);
5303218Sdougm 			    if (group != NULL) {
5313218Sdougm 				share = _sa_add_share(group, mountpoint,
5323034Sdougm 							SA_SHARE_TRANSIENT,
5333034Sdougm 							&err);
5343218Sdougm 				/*
5353218Sdougm 				 * some options may only be on
5363218Sdougm 				 * shares. If the opt string
5373218Sdougm 				 * contains one of those, we
5383218Sdougm 				 * put it just on the share.
5393218Sdougm 				 */
5403218Sdougm 				if (share != NULL &&
5413218Sdougm 				    doshopt == SA_PROP_SHARE_ONLY) {
5423218Sdougm 				    options = strdup(shareopts);
5433218Sdougm 				    if (options != NULL) {
5443218Sdougm 					err = sa_parse_legacy_options(share,
5453108Sdougm 								options, "nfs");
5463218Sdougm 					free(options);
5473108Sdougm 				    }
5483108Sdougm 				}
5493034Sdougm 			    } else {
5503218Sdougm 				err = SA_NO_MEMORY;
5513218Sdougm 			    }
5523218Sdougm 			} else {
5533218Sdougm 			    group = _sa_create_zfs_group(zfsgroup, dataset);
5543218Sdougm 			    if (group == NULL) {
5553218Sdougm 				static int err = 0;
5563108Sdougm 				/*
5573218Sdougm 				 * there is a problem, but we can't do
5583218Sdougm 				 * anything about it at this point so
5593218Sdougm 				 * we issue a warning an move on.
5603108Sdougm 				 */
5613218Sdougm 				if (err == 0) {
5623218Sdougm 				    /* only print error once */
5633218Sdougm 				    (void) fprintf(stderr,
5643407Sdougm 					dgettext(TEXT_DOMAIN,
5653407Sdougm 						"Cannot create ZFS subgroup "
5663218Sdougm 						"during initialization:"
5673218Sdougm 						" %s\n"),
5683218Sdougm 					sa_errorstr(SA_SYSTEM_ERR));
5693218Sdougm 				    err = 1;
5703218Sdougm 				}
5713218Sdougm 				continue;
5723218Sdougm 			    }
5733218Sdougm 			    set_node_attr(group, "zfs", "true");
5743218Sdougm 			    share = _sa_add_share(group, mountpoint,
5753218Sdougm 						SA_SHARE_TRANSIENT, &err);
5763218Sdougm 			    if (err == SA_OK) {
5773218Sdougm 				if (strcmp(shareopts, "on") != 0) {
5783218Sdougm 				    options = strdup(shareopts);
5793218Sdougm 				    if (options != NULL) {
5803218Sdougm 					err = sa_parse_legacy_options(group,
5813034Sdougm 									options,
5823034Sdougm 									"nfs");
5833218Sdougm 					free(options);
5843218Sdougm 				    }
5853218Sdougm 				    if (err == SA_PROP_SHARE_ONLY) {
5863108Sdougm 					/*
5873108Sdougm 					 * Same as above, some
5883108Sdougm 					 * properties may only be on
5893108Sdougm 					 * shares, but due to the ZFS
5903108Sdougm 					 * sub-groups being
5913108Sdougm 					 * artificial, we sometimes
5923108Sdougm 					 * get this and have to deal
5933108Sdougm 					 * with it. We do it by
5943108Sdougm 					 * attempting to put it on the
5953108Sdougm 					 * share.
5963108Sdougm 					 */
5973218Sdougm 					options = strdup(shareopts);
5983218Sdougm 					if (options != NULL)
5993218Sdougm 					    err = sa_parse_legacy_options(
6003108Sdougm 									share,
6013108Sdougm 									options,
6023108Sdougm 									"nfs");
6033108Sdougm 					    free(options);
6043108Sdougm 					}
6053218Sdougm 				    /* unmark the share's changed state */
6063218Sdougm 				    set_node_attr(share, "changed", NULL);
6073034Sdougm 				}
6083034Sdougm 			    }
6093034Sdougm 			}
6103034Sdougm 		    }
6113034Sdougm 		}
6123034Sdougm 	    }
6133034Sdougm 	}
6143218Sdougm 	/*
6153218Sdougm 	 * Don't need to free the "zlist" variable since it is only a
6163218Sdougm 	 * pointer to a cached value that will be freed when
6173218Sdougm 	 * sa_fini() is called.
6183218Sdougm 	 */
6193034Sdougm 	return (legacy);
6203034Sdougm }
6213034Sdougm 
6223034Sdougm #define	COMMAND		"/usr/sbin/zfs"
6233034Sdougm 
6243034Sdougm /*
6253034Sdougm  * sa_zfs_set_sharenfs(group, path, on)
6263034Sdougm  *
6273034Sdougm  * Update the "sharenfs" property on the path. If on is true, then set
6283034Sdougm  * to the properties on the group or "on" if no properties are
6293034Sdougm  * defined. Set to "off" if on is false.
6303034Sdougm  */
6313034Sdougm 
6323034Sdougm int
6333034Sdougm sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
6343034Sdougm {
6353034Sdougm 	int ret = SA_NOT_IMPLEMENTED;
6363034Sdougm 	char *command;
6373034Sdougm 
6383034Sdougm 	command = malloc(ZFS_MAXPROPLEN * 2);
6393034Sdougm 	if (command != NULL) {
6403034Sdougm 	    char *opts = NULL;
641*3910Sdougm 	    char *dataset = NULL;
6423034Sdougm 	    FILE *pfile;
643*3910Sdougm 	    sa_handle_impl_t impl_handle;
6443034Sdougm 	    /* for now, NFS is always available for "zfs" */
6453034Sdougm 	    if (on) {
6463034Sdougm 		opts = sa_proto_legacy_format("nfs", group, 1);
6473034Sdougm 		if (opts != NULL && strlen(opts) == 0) {
6483034Sdougm 		    free(opts);
6493034Sdougm 		    opts = strdup("on");
6503034Sdougm 		}
6513034Sdougm 	    }
652*3910Sdougm 
653*3910Sdougm 	    impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
654*3910Sdougm 	    if (impl_handle != NULL)
655*3910Sdougm 		dataset = get_zfs_dataset(impl_handle, path);
656*3910Sdougm 	    else
657*3910Sdougm 		ret = SA_SYSTEM_ERR;
658*3910Sdougm 
6593034Sdougm 	    if (dataset != NULL) {
6603034Sdougm 		(void) snprintf(command, ZFS_MAXPROPLEN * 2,
6613034Sdougm 				"%s set sharenfs=\"%s\" %s", COMMAND,
6623034Sdougm 				opts != NULL ? opts : "off",
6633034Sdougm 				dataset);
6643034Sdougm 		pfile = popen(command, "r");
6653034Sdougm 		if (pfile != NULL) {
6663034Sdougm 		    ret = pclose(pfile);
6673034Sdougm 		    if (ret != 0)
6683034Sdougm 			ret = SA_SYSTEM_ERR;
6693034Sdougm 		}
6703034Sdougm 	    }
6713034Sdougm 	    if (opts != NULL)
6723034Sdougm 		free(opts);
6733034Sdougm 	    if (dataset != NULL)
6743034Sdougm 		free(dataset);
6753034Sdougm 	    free(command);
6763034Sdougm 	}
6773034Sdougm 	return (ret);
6783034Sdougm }
6793034Sdougm 
6803034Sdougm /*
6813034Sdougm  * sa_zfs_update(group)
6823034Sdougm  *
6833034Sdougm  * call back to ZFS to update the share if necessary.
6843034Sdougm  * Don't do it if it isn't a real change.
6853034Sdougm  */
6863034Sdougm int
6873034Sdougm sa_zfs_update(sa_group_t group)
6883034Sdougm {
6893034Sdougm 	sa_optionset_t protopt;
6903034Sdougm 	sa_group_t parent;
6913034Sdougm 	char *command;
6923034Sdougm 	char *optstring;
6933034Sdougm 	int ret = SA_OK;
6943034Sdougm 	int doupdate = 0;
6953034Sdougm 	FILE *pfile;
6963034Sdougm 
6973034Sdougm 	if (sa_is_share(group))
6983034Sdougm 	    parent = sa_get_parent_group(group);
6993034Sdougm 	else
7003034Sdougm 	    parent = group;
7013034Sdougm 
7023034Sdougm 	if (parent != NULL) {
7033034Sdougm 	    command = malloc(ZFS_MAXPROPLEN * 2);
7043034Sdougm 	    if (command == NULL)
7053034Sdougm 		return (SA_NO_MEMORY);
7063034Sdougm 
7073034Sdougm 	    *command = '\0';
7083034Sdougm 	    for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
7093034Sdougm 		protopt = sa_get_next_optionset(protopt)) {
7103034Sdougm 
7113034Sdougm 		char *proto = sa_get_optionset_attr(protopt, "type");
7123034Sdougm 		char *path;
7133034Sdougm 		char *dataset = NULL;
7143034Sdougm 		char *zfsopts = NULL;
7153034Sdougm 
7163034Sdougm 		if (sa_is_share(group)) {
7173034Sdougm 		    path = sa_get_share_attr((sa_share_t)group, "path");
7183034Sdougm 		    if (path != NULL) {
719*3910Sdougm 			sa_handle_impl_t impl_handle;
720*3910Sdougm 
721*3910Sdougm 			impl_handle = sa_find_group_handle(group);
722*3910Sdougm 			if (impl_handle != NULL)
723*3910Sdougm 			    dataset = get_zfs_dataset(impl_handle, path);
724*3910Sdougm 			else
725*3910Sdougm 			    ret = SA_SYSTEM_ERR;
726*3910Sdougm 
7273034Sdougm 			sa_free_attr_string(path);
7283034Sdougm 		    }
7293034Sdougm 		} else {
7303034Sdougm 		    dataset = sa_get_group_attr(group, "name");
7313034Sdougm 		}
7323034Sdougm 		/* update only when there is an optstring found */
7333034Sdougm 		doupdate = 0;
7343034Sdougm 		if (proto != NULL && dataset != NULL) {
7353034Sdougm 		    optstring = sa_proto_legacy_format(proto, group, 1);
7363034Sdougm 		    zfsopts = get_zfs_property(dataset, ZFS_PROP_SHARENFS);
7373034Sdougm 
7383034Sdougm 		    if (optstring != NULL && zfsopts != NULL) {
7393034Sdougm 			if (strcmp(optstring, zfsopts) != 0)
7403034Sdougm 			    doupdate++;
7413034Sdougm 		    }
7423034Sdougm 
7433034Sdougm 		    if (doupdate) {
7443034Sdougm 			if (optstring != NULL && strlen(optstring) > 0) {
7453034Sdougm 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
7463034Sdougm 					    "%s set sharenfs=%s %s", COMMAND,
7473034Sdougm 					    optstring, dataset);
7483034Sdougm 			} else {
7493034Sdougm 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
7503034Sdougm 					    "%s set sharenfs=on %s", COMMAND,
7513034Sdougm 					    dataset);
7523034Sdougm 			}
7533034Sdougm 			pfile = popen(command, "r");
7543034Sdougm 			if (pfile != NULL)
7553034Sdougm 			    ret = pclose(pfile);
7563034Sdougm 			switch (ret) {
7573034Sdougm 			default:
7583034Sdougm 			case 1:
7593034Sdougm 			    ret = SA_SYSTEM_ERR;
7603034Sdougm 			    break;
7613034Sdougm 			case 2:
7623034Sdougm 			    ret = SA_SYNTAX_ERR;
7633034Sdougm 			    break;
7643034Sdougm 			case 0:
7653034Sdougm 			    break;
7663034Sdougm 			}
7673034Sdougm 		    }
7683034Sdougm 		    if (optstring != NULL) {
7693034Sdougm 			free(optstring);
7703034Sdougm 		    }
7713034Sdougm 		    if (zfsopts != NULL)
7723034Sdougm 			free(zfsopts);
7733034Sdougm 		}
7743034Sdougm 		if (proto != NULL)
7753034Sdougm 		    sa_free_attr_string(proto);
7763034Sdougm 		if (dataset != NULL)
7773034Sdougm 		    free(dataset);
7783034Sdougm 	    }
7793034Sdougm 	    free(command);
7803034Sdougm 	}
7813034Sdougm 	return (ret);
7823034Sdougm }
7833034Sdougm 
7843034Sdougm /*
7853034Sdougm  * sa_group_is_zfs(group)
7863034Sdougm  *
7873034Sdougm  * Given the group, determine if the zfs attribute is set.
7883034Sdougm  */
7893034Sdougm 
7903034Sdougm int
7913034Sdougm sa_group_is_zfs(sa_group_t group)
7923034Sdougm {
7933034Sdougm 	char *zfs;
7943034Sdougm 	int ret = 0;
7953034Sdougm 
7963034Sdougm 	zfs = sa_get_group_attr(group, "zfs");
7973034Sdougm 	if (zfs != NULL) {
7983034Sdougm 	    ret = 1;
7993034Sdougm 	    sa_free_attr_string(zfs);
8003034Sdougm 	}
8013034Sdougm 	return (ret);
8023034Sdougm }
8033034Sdougm 
8043034Sdougm /*
8053034Sdougm  * sa_path_is_zfs(path)
8063034Sdougm  *
8073034Sdougm  * Check to see if the file system path represents is of type "zfs".
8083034Sdougm  */
8093034Sdougm 
8103034Sdougm int
8113034Sdougm sa_path_is_zfs(char *path)
8123034Sdougm {
8133034Sdougm 	char *fstype;
8143034Sdougm 	int ret = 0;
8153034Sdougm 
8163034Sdougm 	fstype = sa_fstype(path);
8173034Sdougm 	if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
8183034Sdougm 	    ret = 1;
8193034Sdougm 	}
8203034Sdougm 	if (fstype != NULL)
8213034Sdougm 	    sa_free_fstype(fstype);
8223034Sdougm 	return (ret);
8233034Sdougm }
824