xref: /illumos-gate/usr/src/lib/libshare/common/libshare_zfs.c (revision 7b4214534ccdf6f8aa6e566d7501aab328c72e84)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
28  * Copyright 2017 RackTop Systems.
29  * Copyright 2019 Nexenta Systems, Inc.
30  */
31 
32 #include <stdio.h>
33 #include <libzfs.h>
34 #include <string.h>
35 #include <strings.h>
36 #include <errno.h>
37 #include <zone.h>
38 #include <libshare.h>
39 #include "libshare_impl.h"
40 #include <libintl.h>
41 #include <sys/mnttab.h>
42 #include <sys/mntent.h>
43 #include <assert.h>
44 
45 extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *, uint64_t);
46 extern sa_group_t _sa_create_zfs_group(sa_group_t, char *);
47 extern char *sa_fstype(char *);
48 extern void set_node_attr(void *, char *, char *);
49 extern int sa_is_share(void *);
50 extern void sa_update_sharetab_ts(sa_handle_t);
51 
52 /*
53  * File system specific code for ZFS. The original code was stolen
54  * from the "zfs" command and modified to better suit this library's
55  * usage.
56  */
57 
58 typedef struct get_all_cbdata {
59 	zfs_handle_t	**cb_handles;
60 	size_t		cb_alloc;
61 	size_t		cb_used;
62 	uint_t		cb_types;
63 } get_all_cbdata_t;
64 
65 /*
66  * sa_zfs_init(impl_handle)
67  *
68  * Initialize an access handle into libzfs.  The handle needs to stay
69  * around until sa_zfs_fini() in order to maintain the cache of
70  * mounts.
71  */
72 
73 int
74 sa_zfs_init(sa_handle_impl_t impl_handle)
75 {
76 	impl_handle->zfs_libhandle = libzfs_init();
77 	if (impl_handle->zfs_libhandle != NULL) {
78 		libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE);
79 		return (B_TRUE);
80 	}
81 	return (B_FALSE);
82 }
83 
84 /*
85  * sa_zfs_fini(impl_handle)
86  *
87  * cleanup data structures and the libzfs handle used for accessing
88  * zfs file share info.
89  */
90 
91 void
92 sa_zfs_fini(sa_handle_impl_t impl_handle)
93 {
94 	if (impl_handle->zfs_libhandle != NULL) {
95 		if (impl_handle->zfs_list != NULL) {
96 			zfs_handle_t **zhp = impl_handle->zfs_list;
97 			size_t i;
98 
99 			/*
100 			 * Contents of zfs_list need to be freed so we
101 			 * don't lose ZFS handles.
102 			 */
103 			for (i = 0; i < impl_handle->zfs_list_count; i++) {
104 				zfs_close(zhp[i]);
105 			}
106 			free(impl_handle->zfs_list);
107 			impl_handle->zfs_list = NULL;
108 			impl_handle->zfs_list_count = 0;
109 		}
110 
111 		libzfs_fini(impl_handle->zfs_libhandle);
112 		impl_handle->zfs_libhandle = NULL;
113 	}
114 }
115 
116 /*
117  * get_one_filesystem(zfs_handle_t, data)
118  *
119  * an iterator function called while iterating through the ZFS
120  * root. It accumulates into an array of file system handles that can
121  * be used to derive info about those file systems.
122  *
123  * Note that as this function is called, we close all zhp handles that
124  * are not going to be places into the cp_handles list. We don't want
125  * to close the ones we are keeping, but all others would be leaked if
126  * not closed here.
127  */
128 
129 static int
130 get_one_filesystem(zfs_handle_t *zhp, void *data)
131 {
132 	get_all_cbdata_t *cbp = data;
133 	zfs_type_t type = zfs_get_type(zhp);
134 
135 	/*
136 	 * Interate over any nested datasets.
137 	 */
138 	if (type == ZFS_TYPE_FILESYSTEM &&
139 	    zfs_iter_filesystems(zhp, get_one_filesystem, data) != 0) {
140 		zfs_close(zhp);
141 		return (1);
142 	}
143 
144 	/*
145 	 * Skip any datasets whose type does not match.
146 	 */
147 	if ((type & cbp->cb_types) == 0) {
148 		zfs_close(zhp);
149 		return (0);
150 	}
151 
152 	if (cbp->cb_alloc == cbp->cb_used) {
153 		zfs_handle_t **handles;
154 
155 		if (cbp->cb_alloc == 0)
156 			cbp->cb_alloc = 64;
157 		else
158 			cbp->cb_alloc *= 2;
159 
160 		handles = (zfs_handle_t **)calloc(1,
161 		    cbp->cb_alloc * sizeof (void *));
162 
163 		if (handles == NULL) {
164 			zfs_close(zhp);
165 			return (0);
166 		}
167 		if (cbp->cb_handles) {
168 			bcopy(cbp->cb_handles, handles,
169 			    cbp->cb_used * sizeof (void *));
170 			free(cbp->cb_handles);
171 		}
172 
173 		cbp->cb_handles = handles;
174 	}
175 
176 	cbp->cb_handles[cbp->cb_used++] = zhp;
177 
178 	return (0);
179 }
180 
181 /*
182  * get_all_filesystems(zfs_handle_t ***fslist, size_t *count)
183  *
184  * iterate through all ZFS file systems starting at the root. Returns
185  * a count and an array of handle pointers. Allocating is only done
186  * once. The caller does not need to free since it will be done at
187  * sa_zfs_fini() time.
188  */
189 
190 static void
191 get_all_filesystems(sa_handle_impl_t impl_handle,
192     zfs_handle_t ***fslist, size_t *count)
193 {
194 	get_all_cbdata_t cb = { 0 };
195 	cb.cb_types = ZFS_TYPE_FILESYSTEM;
196 
197 	if (impl_handle->zfs_list != NULL) {
198 		*fslist = impl_handle->zfs_list;
199 		*count = impl_handle->zfs_list_count;
200 		return;
201 	}
202 
203 	(void) zfs_iter_root(impl_handle->zfs_libhandle,
204 	    get_one_filesystem, &cb);
205 
206 	impl_handle->zfs_list = *fslist = cb.cb_handles;
207 	impl_handle->zfs_list_count = *count = cb.cb_used;
208 }
209 
210 /*
211  * mountpoint_compare(a, b)
212  *
213  * compares the mountpoint on two zfs file systems handles.
214  * returns values following strcmp() model.
215  */
216 
217 static int
218 mountpoint_compare(const void *a, const void *b)
219 {
220 	zfs_handle_t **za = (zfs_handle_t **)a;
221 	zfs_handle_t **zb = (zfs_handle_t **)b;
222 	char mounta[MAXPATHLEN];
223 	char mountb[MAXPATHLEN];
224 
225 	verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
226 	    sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
227 	verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
228 	    sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
229 
230 	return (strcmp(mounta, mountb));
231 }
232 
233 /*
234  * return legacy mountpoint.  Caller provides space for mountpoint and
235  * dataset.
236  */
237 int
238 get_legacy_mountpoint(const char *path, char *dataset, size_t dlen,
239     char *mountpoint, size_t mlen)
240 {
241 	FILE *fp;
242 	struct mnttab entry;
243 	int rc = 1;
244 
245 	if ((fp = fopen(MNTTAB, "r")) == NULL) {
246 		return (1);
247 	}
248 
249 	while (getmntent(fp, &entry) == 0) {
250 
251 		if (entry.mnt_fstype == NULL ||
252 		    strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
253 			continue;
254 
255 		if (strcmp(entry.mnt_mountp, path) == 0) {
256 			if (mlen > 0)
257 				(void) strlcpy(mountpoint, entry.mnt_mountp,
258 				    mlen);
259 			if (dlen > 0)
260 				(void) strlcpy(dataset, entry.mnt_special,
261 				    dlen);
262 			rc = 0;
263 			break;
264 		}
265 	}
266 	(void) fclose(fp);
267 	return (rc);
268 }
269 
270 
271 /*
272  * Verifies that a specific zfs filesystem handle meets the criteria necessary
273  * to be used by libshare operations. See get_zfs_dataset.
274  */
275 static char *
276 verify_zfs_handle(zfs_handle_t *hdl, const char *path, boolean_t search_mnttab)
277 {
278 	char mountpoint[ZFS_MAXPROPLEN];
279 	char canmount[ZFS_MAXPROPLEN] = { 0 };
280 	/* must have a mountpoint */
281 	if (zfs_prop_get(hdl, ZFS_PROP_MOUNTPOINT, mountpoint,
282 	    sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
283 		/* no mountpoint */
284 		return (NULL);
285 	}
286 
287 	/* mountpoint must be a path */
288 	if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
289 	    strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
290 		/*
291 		 * Search mmttab for mountpoint and get dataset.
292 		 */
293 
294 		if (search_mnttab == B_TRUE &&
295 		    get_legacy_mountpoint(path, mountpoint,
296 		    sizeof (mountpoint), NULL, 0) == 0) {
297 			return (strdup(mountpoint));
298 		}
299 		return (NULL);
300 	}
301 
302 	/* canmount must be set */
303 	if (zfs_prop_get(hdl, ZFS_PROP_CANMOUNT, canmount,
304 	    sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 ||
305 	    strcmp(canmount, "off") == 0)
306 		return (NULL);
307 
308 	/*
309 	 * have a mountable handle but want to skip those marked none
310 	 * and legacy
311 	 */
312 	if (strcmp(mountpoint, path) == 0) {
313 		return (strdup((char *)zfs_get_name(hdl)));
314 	}
315 
316 	return (NULL);
317 }
318 
319 /*
320  * get_zfs_dataset(impl_handle, path)
321  *
322  * get the name of the ZFS dataset the path is equivalent to.  The
323  * dataset name is used for get/set of ZFS properties since libzfs
324  * requires a dataset to do a zfs_open().
325  */
326 
327 static char *
328 get_zfs_dataset(sa_handle_impl_t impl_handle, char *path,
329     boolean_t search_mnttab)
330 {
331 	size_t i, count = 0;
332 	zfs_handle_t **zlist;
333 	char *cutpath;
334 	zfs_handle_t *handle_from_path;
335 	char *ret = NULL;
336 
337 	/*
338 	 * First we optimistically assume that the mount path for the filesystem
339 	 * is the same as the name of the filesystem (minus some number of
340 	 * leading slashes). If this is true, then zfs_open should properly open
341 	 * the filesystem. We duplicate the error checking done later in the
342 	 * function for consistency. If anything fails, we resort to the
343 	 * (extremely slow) search of all the filesystems.
344 	 */
345 	cutpath = path + strspn(path, "/");
346 
347 	assert(impl_handle->zfs_libhandle != NULL);
348 	libzfs_print_on_error(impl_handle->zfs_libhandle, B_FALSE);
349 	handle_from_path = zfs_open(impl_handle->zfs_libhandle, cutpath,
350 	    ZFS_TYPE_FILESYSTEM);
351 	libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE);
352 	if (handle_from_path != NULL) {
353 		ret = verify_zfs_handle(handle_from_path, path, search_mnttab);
354 		zfs_close(handle_from_path);
355 		if (ret != NULL) {
356 			return (ret);
357 		}
358 	}
359 	/*
360 	 * Couldn't find a filesystem optimistically, check all the handles we
361 	 * can.
362 	 */
363 	get_all_filesystems(impl_handle, &zlist, &count);
364 	for (i = 0; i < count; i++) {
365 		assert(zlist[i]);
366 		if ((ret = verify_zfs_handle(zlist[i], path,
367 		    search_mnttab)) != NULL)
368 			return (ret);
369 	}
370 
371 	/* Couldn't find a matching dataset */
372 	return (NULL);
373 }
374 
375 /*
376  * get_zfs_property(dataset, property)
377  *
378  * Get the file system property specified from the ZFS dataset.
379  */
380 
381 static char *
382 get_zfs_property(char *dataset, zfs_prop_t property)
383 {
384 	zfs_handle_t *handle = NULL;
385 	char shareopts[ZFS_MAXPROPLEN];
386 	libzfs_handle_t *libhandle;
387 
388 	libhandle = libzfs_init();
389 	if (libhandle != NULL) {
390 		handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
391 		if (handle != NULL) {
392 			if (zfs_prop_get(handle, property, shareopts,
393 			    sizeof (shareopts), NULL, NULL, 0,
394 			    B_FALSE) == 0) {
395 				zfs_close(handle);
396 				libzfs_fini(libhandle);
397 				return (strdup(shareopts));
398 			}
399 			zfs_close(handle);
400 		}
401 		libzfs_fini(libhandle);
402 	}
403 	return (NULL);
404 }
405 
406 /*
407  * sa_zfs_is_shared(handle, path)
408  *
409  * Check to see if the ZFS path provided has the sharenfs option set
410  * or not.
411  */
412 
413 int
414 sa_zfs_is_shared(sa_handle_t sahandle, char *path)
415 {
416 	int ret = 0;
417 	char *dataset;
418 	zfs_handle_t *handle = NULL;
419 	char shareopts[ZFS_MAXPROPLEN];
420 	libzfs_handle_t *libhandle;
421 
422 	dataset = get_zfs_dataset((sa_handle_t)sahandle, path, B_FALSE);
423 	if (dataset != NULL) {
424 		libhandle = libzfs_init();
425 		if (libhandle != NULL) {
426 			handle = zfs_open(libhandle, dataset,
427 			    ZFS_TYPE_FILESYSTEM);
428 			if (handle != NULL) {
429 				if (zfs_prop_get(handle, ZFS_PROP_SHARENFS,
430 				    shareopts, sizeof (shareopts), NULL, NULL,
431 				    0, B_FALSE) == 0 &&
432 				    strcmp(shareopts, "off") != 0) {
433 					ret = 1; /* it is shared */
434 				}
435 				zfs_close(handle);
436 			}
437 			libzfs_fini(libhandle);
438 		}
439 		free(dataset);
440 	}
441 	return (ret);
442 }
443 
444 /*
445  * find_or_create_group(handle, groupname, proto, *err)
446  *
447  * While walking the ZFS tree, we need to add shares to a defined
448  * group. If the group doesn't exist, create it first, making sure it
449  * is marked as a ZFS group.
450  *
451  * Note that all ZFS shares are in a subgroup of the top level group
452  * called "zfs".
453  */
454 
455 static sa_group_t
456 find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err)
457 {
458 	sa_group_t group;
459 	sa_optionset_t optionset;
460 	int ret = SA_OK;
461 
462 	/*
463 	 * we check to see if the "zfs" group exists. Since this
464 	 * should be the top level group, we don't want the
465 	 * parent. This is to make sure the zfs group has been created
466 	 * and to created if it hasn't been.
467 	 */
468 	group = sa_get_group(handle, groupname);
469 	if (group == NULL) {
470 		group = sa_create_group(handle, groupname, &ret);
471 
472 		/* make sure this is flagged as a ZFS group */
473 		if (group != NULL)
474 			ret = sa_set_group_attr(group, "zfs", "true");
475 	}
476 	if (group != NULL) {
477 		if (proto != NULL) {
478 			optionset = sa_get_optionset(group, proto);
479 			if (optionset == NULL)
480 				optionset = sa_create_optionset(group, proto);
481 		}
482 	}
483 	if (err != NULL)
484 		*err = ret;
485 	return (group);
486 }
487 
488 /*
489  * find_or_create_zfs_subgroup(groupname, optstring, *err)
490  *
491  * ZFS shares will be in a subgroup of the "zfs" master group.  This
492  * function looks to see if the groupname exists and returns it if it
493  * does or else creates a new one with the specified name and returns
494  * that.  The "zfs" group will exist before we get here, but we make
495  * sure just in case.
496  *
497  * err must be a valid pointer.
498  */
499 
500 static sa_group_t
501 find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname, char *proto,
502     char *optstring, int *err)
503 {
504 	sa_group_t group = NULL;
505 	sa_group_t zfs;
506 	char *name;
507 	char *options;
508 
509 	/* start with the top-level "zfs" group */
510 	zfs = sa_get_group(handle, "zfs");
511 	*err = SA_OK;
512 	if (zfs != NULL) {
513 		for (group = sa_get_sub_group(zfs); group != NULL;
514 		    group = sa_get_next_group(group)) {
515 			name = sa_get_group_attr(group, "name");
516 			if (name != NULL && strcmp(name, groupname) == 0) {
517 				/* have the group so break out of here */
518 				sa_free_attr_string(name);
519 				break;
520 			}
521 			if (name != NULL)
522 				sa_free_attr_string(name);
523 		}
524 
525 		if (group == NULL) {
526 			/*
527 			 * Need to create the sub-group since it doesn't exist
528 			 */
529 			group = _sa_create_zfs_group(zfs, groupname);
530 			if (group == NULL) {
531 				*err = SA_NO_MEMORY;
532 				return (NULL);
533 			}
534 			set_node_attr(group, "zfs", "true");
535 		}
536 		if (strcmp(optstring, "on") == 0)
537 			optstring = "rw";
538 		options = strdup(optstring);
539 		if (options != NULL) {
540 			*err = sa_parse_legacy_options(group, options,
541 			    proto);
542 			/* If no optionset, add one. */
543 			if (sa_get_optionset(group, proto) == NULL)
544 				(void) sa_create_optionset(group, proto);
545 
546 			/*
547 			 * Do not forget to update an optionset of
548 			 * the parent group so that it contains
549 			 * all protocols its subgroups have.
550 			 */
551 			if (sa_get_optionset(zfs, proto) == NULL)
552 				(void) sa_create_optionset(zfs, proto);
553 
554 			free(options);
555 		} else {
556 			*err = SA_NO_MEMORY;
557 		}
558 	}
559 	return (group);
560 }
561 
562 /*
563  * zfs_construct_resource(share, name, base, dataset)
564  *
565  * Add a resource to the share using name as a template. If name ==
566  * NULL, then construct a name based on the dataset value.
567  * name.
568  */
569 static void
570 zfs_construct_resource(sa_share_t share, char *dataset)
571 {
572 	char buff[SA_MAX_RESOURCE_NAME + 1];
573 	int ret = SA_OK;
574 
575 	(void) snprintf(buff, SA_MAX_RESOURCE_NAME, "%s", dataset);
576 	sa_fix_resource_name(buff);
577 	(void) sa_add_resource(share, buff, SA_SHARE_TRANSIENT, &ret);
578 }
579 
580 /*
581  * zfs_inherited(handle, source, sourcestr)
582  *
583  * handle case of inherited share{nfs,smb}. Pulled out of sa_get_zfs_shares
584  * for readability.
585  */
586 static int
587 zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr,
588     char *shareopts, char *mountpoint, char *proto, char *dataset)
589 {
590 	int doshopt = 0;
591 	int err = SA_OK;
592 	sa_group_t group;
593 	sa_resource_t resource;
594 	uint64_t features;
595 
596 	/*
597 	 * Need to find the "real" parent sub-group. It may not be
598 	 * mounted, but it was identified in the "sourcestr"
599 	 * variable. The real parent not mounted can occur if
600 	 * "canmount=off and sharenfs=on".
601 	 */
602 	group = find_or_create_zfs_subgroup(handle, sourcestr, proto,
603 	    shareopts, &doshopt);
604 	if (group != NULL) {
605 		/*
606 		 * We may need the first share for resource
607 		 * prototype. We only care about it if it has a
608 		 * resource that sets a prefix value.
609 		 */
610 		if (share == NULL)
611 			share = _sa_add_share(group, mountpoint,
612 			    SA_SHARE_TRANSIENT, &err,
613 			    (uint64_t)SA_FEATURE_NONE);
614 		/*
615 		 * some options may only be on shares. If the opt
616 		 * string contains one of those, we put it just on the
617 		 * share.
618 		 */
619 		if (share != NULL && doshopt == SA_PROP_SHARE_ONLY) {
620 			char *options;
621 			options = strdup(shareopts);
622 			if (options != NULL) {
623 				set_node_attr(share, "dataset", dataset);
624 				err = sa_parse_legacy_options(share, options,
625 				    proto);
626 				set_node_attr(share, "dataset", NULL);
627 				free(options);
628 			}
629 			if (sa_get_optionset(group, proto) == NULL)
630 				(void) sa_create_optionset(group, proto);
631 		}
632 		features = sa_proto_get_featureset(proto);
633 		if (share != NULL && features & SA_FEATURE_RESOURCE) {
634 			/*
635 			 * We have a share and the protocol requires
636 			 * that at least one resource exist (probably
637 			 * SMB). We need to make sure that there is at
638 			 * least one.
639 			 */
640 			resource = sa_get_share_resource(share, NULL);
641 			if (resource == NULL) {
642 				zfs_construct_resource(share, dataset);
643 			}
644 		}
645 	} else {
646 		err = SA_NO_MEMORY;
647 	}
648 	return (err);
649 }
650 
651 /*
652  * zfs_notinherited(group, share, mountpoint, shareopts, proto, dataset,
653  *     grouperr)
654  *
655  * handle case where this is the top of a sub-group in ZFS. Pulled out
656  * of sa_get_zfs_shares for readability. We need the grouperr from the
657  * creation of the subgroup to know whether to add the public
658  * property, etc. to the specific share.
659  */
660 static int
661 zfs_notinherited(sa_group_t group, sa_share_t share, char *mountpoint,
662     char *shareopts, char *proto, char *dataset, int grouperr)
663 {
664 	int err = SA_OK;
665 	sa_resource_t resource;
666 	uint64_t features;
667 
668 	set_node_attr(group, "zfs", "true");
669 	if (share == NULL)
670 		share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT,
671 		    &err, (uint64_t)SA_FEATURE_NONE);
672 
673 	if (err != SA_OK)
674 		return (err);
675 
676 	if (strcmp(shareopts, "on") == 0)
677 		shareopts = "";
678 	if (shareopts != NULL) {
679 		char *options;
680 		if (grouperr == SA_PROP_SHARE_ONLY) {
681 			/*
682 			 * Some properties may only be on shares, but
683 			 * due to the ZFS sub-groups being artificial,
684 			 * we sometimes get this and have to deal with
685 			 * it. We do it by attempting to put it on the
686 			 * share.
687 			 */
688 			options = strdup(shareopts);
689 			if (options != NULL) {
690 				err = sa_parse_legacy_options(share,
691 				    options, proto);
692 				free(options);
693 			}
694 		}
695 		/* Unmark the share's changed state */
696 		set_node_attr(share, "changed", NULL);
697 	}
698 	features = sa_proto_get_featureset(proto);
699 	if (share != NULL && features & SA_FEATURE_RESOURCE) {
700 		/*
701 		 * We have a share and the protocol requires that at
702 		 * least one resource exist (probably SMB). We need to
703 		 * make sure that there is at least one.
704 		 */
705 		resource = sa_get_share_resource(share, NULL);
706 		if (resource == NULL) {
707 			zfs_construct_resource(share, dataset);
708 		}
709 	}
710 	return (err);
711 }
712 
713 /*
714  * zfs_grp_error(err)
715  *
716  * Print group create error, but only once. If err is 0 do the
717  * print else don't.
718  */
719 
720 static void
721 zfs_grp_error(int err)
722 {
723 	if (err == 0) {
724 		/* only print error once */
725 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
726 		    "Cannot create ZFS subgroup during initialization:"
727 		    " %s\n"), sa_errorstr(SA_SYSTEM_ERR));
728 	}
729 }
730 
731 /*
732  * zfs_process_share(handle, share, mountpoint, proto, source,
733  *     shareopts, sourcestr)
734  *
735  * Creates the subgroup, if necessary and adds shares, resources
736  * and properties.
737  */
738 int
739 sa_zfs_process_share(sa_handle_t handle, sa_group_t group, sa_share_t share,
740     char *mountpoint, char *proto, zprop_source_t source, char *shareopts,
741     char *sourcestr, char *dataset)
742 {
743 	int err = SA_OK;
744 
745 	if (source & ZPROP_SRC_INHERITED) {
746 		err = zfs_inherited(handle, share, sourcestr, shareopts,
747 		    mountpoint, proto, dataset);
748 	} else {
749 		group = find_or_create_zfs_subgroup(handle, dataset, proto,
750 		    shareopts, &err);
751 		if (group == NULL) {
752 			static boolean_t reported_error = B_FALSE;
753 			/*
754 			 * There is a problem, but we can't do
755 			 * anything about it at this point so we issue
756 			 * a warning and move on.
757 			 */
758 			zfs_grp_error(reported_error);
759 			reported_error = B_TRUE;
760 		}
761 		set_node_attr(group, "zfs", "true");
762 		/*
763 		 * Add share with local opts via zfs_notinherited.
764 		 */
765 		err = zfs_notinherited(group, share, mountpoint, shareopts,
766 		    proto, dataset, err);
767 	}
768 	return (err);
769 }
770 
771 /*
772  * Walk the mnttab for all zfs mounts and determine which are
773  * shared. Find or create the appropriate group/sub-group to contain
774  * the shares.
775  *
776  * All shares are in a sub-group that will hold the properties. This
777  * allows representing the inherited property model.
778  *
779  * One area of complication is if "sharenfs" is set at one level of
780  * the directory tree and "sharesmb" is set at a different level, the
781  * a sub-group must be formed at the lower level for both
782  * protocols. That is the nature of the problem in CR 6667349.
783  */
784 static int
785 sa_get_zfs_share_common(sa_handle_t handle, zfs_handle_t *fs_handle, char *path,
786     sa_group_t zfsgroup)
787 {
788 	boolean_t smb, nfs;
789 	boolean_t smb_inherited, nfs_inherited;
790 	char nfsshareopts[ZFS_MAXPROPLEN];
791 	char smbshareopts[ZFS_MAXPROPLEN];
792 	char nfssourcestr[ZFS_MAXPROPLEN];
793 	char smbsourcestr[ZFS_MAXPROPLEN];
794 	char mountpoint[ZFS_MAXPROPLEN];
795 	int err = SA_OK;
796 	zprop_source_t source;
797 	sa_share_t share;
798 	char *dataset;
799 
800 	source = ZPROP_SRC_ALL;
801 	/* If no mountpoint, skip. */
802 	if (zfs_prop_get(fs_handle, ZFS_PROP_MOUNTPOINT,
803 	    mountpoint, sizeof (mountpoint), NULL, NULL, 0,
804 	    B_FALSE) != 0)
805 		return (SA_SYSTEM_ERR);
806 
807 	if (path != NULL)
808 		(void) strncpy(path, mountpoint, sizeof (mountpoint));
809 	/*
810 	 * zfs_get_name value must not be freed. It is just a
811 	 * pointer to a value in the handle.
812 	 */
813 	if ((dataset = (char *)zfs_get_name(fs_handle)) == NULL)
814 		return (SA_SYSTEM_ERR);
815 
816 	/*
817 	 * only deal with "mounted" file systems since
818 	 * unmounted file systems can't actually be shared.
819 	 */
820 
821 	if (!zfs_is_mounted(fs_handle, NULL))
822 		return (SA_SYSTEM_ERR);
823 
824 	/*
825 	 * Ignore "zoned" datasets in global zone.
826 	 */
827 	if (getzoneid() == GLOBAL_ZONEID &&
828 	    zfs_prop_get_int(fs_handle, ZFS_PROP_ZONED))
829 		return (SA_SYSTEM_ERR);
830 
831 	nfs = nfs_inherited = B_FALSE;
832 
833 	if (zfs_prop_get(fs_handle, ZFS_PROP_SHARENFS, nfsshareopts,
834 	    sizeof (nfsshareopts), &source, nfssourcestr,
835 	    ZFS_MAXPROPLEN, B_FALSE) == 0 &&
836 	    strcmp(nfsshareopts, "off") != 0) {
837 		if (source & ZPROP_SRC_INHERITED)
838 			nfs_inherited = B_TRUE;
839 		else
840 			nfs = B_TRUE;
841 	}
842 
843 	smb = smb_inherited = B_FALSE;
844 	if (zfs_prop_get(fs_handle, ZFS_PROP_SHARESMB, smbshareopts,
845 	    sizeof (smbshareopts), &source, smbsourcestr,
846 	    ZFS_MAXPROPLEN, B_FALSE) == 0 &&
847 	    strcmp(smbshareopts, "off") != 0) {
848 		if (source & ZPROP_SRC_INHERITED)
849 			smb_inherited = B_TRUE;
850 		else
851 			smb = B_TRUE;
852 	}
853 
854 	/*
855 	 * If the mountpoint is already shared, it must be a
856 	 * non-ZFS share. We want to remove the share from its
857 	 * parent group and reshare it under ZFS.
858 	 */
859 	share = sa_find_share(handle, mountpoint);
860 	if (share != NULL &&
861 	    (nfs || smb || nfs_inherited || smb_inherited)) {
862 		err = sa_remove_share(share);
863 		share = NULL;
864 	}
865 
866 	/*
867 	 * At this point, we have the information needed to
868 	 * determine what to do with the share.
869 	 *
870 	 * If smb or nfs is set, we have a new sub-group.
871 	 * If smb_inherit and/or nfs_inherit is set, then
872 	 * place on an existing sub-group. If both are set,
873 	 * the existing sub-group is the closest up the tree.
874 	 */
875 	if (nfs || smb) {
876 		/*
877 		 * Non-inherited is the straightforward
878 		 * case. sa_zfs_process_share handles it
879 		 * directly. Make sure that if the "other"
880 		 * protocol is inherited, that we treat it as
881 		 * non-inherited as well.
882 		 */
883 		if (nfs || nfs_inherited) {
884 			err = sa_zfs_process_share(handle, zfsgroup,
885 			    share, mountpoint, "nfs",
886 			    0, nfsshareopts,
887 			    nfssourcestr, dataset);
888 			share = sa_find_share(handle, mountpoint);
889 		}
890 		if (smb || smb_inherited) {
891 			err = sa_zfs_process_share(handle, zfsgroup,
892 			    share, mountpoint, "smb",
893 			    0, smbshareopts,
894 			    smbsourcestr, dataset);
895 		}
896 	} else if (nfs_inherited || smb_inherited) {
897 		char *grpdataset;
898 		/*
899 		 * If we only have inherited groups, it is
900 		 * important to find the closer of the two if
901 		 * the protocols are set at different
902 		 * levels. The closest sub-group is the one we
903 		 * want to work with.
904 		 */
905 		if (nfs_inherited && smb_inherited) {
906 			if (strcmp(nfssourcestr, smbsourcestr) <= 0)
907 				grpdataset = nfssourcestr;
908 			else
909 				grpdataset = smbsourcestr;
910 		} else if (nfs_inherited) {
911 			grpdataset = nfssourcestr;
912 		} else if (smb_inherited) {
913 			grpdataset = smbsourcestr;
914 		}
915 		if (nfs_inherited) {
916 			err = sa_zfs_process_share(handle, zfsgroup,
917 			    share, mountpoint, "nfs",
918 			    ZPROP_SRC_INHERITED, nfsshareopts,
919 			    grpdataset, dataset);
920 			share = sa_find_share(handle, mountpoint);
921 		}
922 		if (smb_inherited) {
923 			err = sa_zfs_process_share(handle, zfsgroup,
924 			    share, mountpoint, "smb",
925 			    ZPROP_SRC_INHERITED, smbshareopts,
926 			    grpdataset, dataset);
927 		}
928 	}
929 	return (err);
930 }
931 
932 /*
933  * Handles preparing generic objects such as the libzfs handle and group for
934  * sa_get_one_zfs_share, sa_get_zfs_share_for_name, and sa_get_zfs_shares.
935  */
936 static int
937 prep_zfs_handle_and_group(sa_handle_t handle, char *groupname,
938     libzfs_handle_t **zfs_libhandle, sa_group_t *zfsgroup, int *err)
939 {
940 	/*
941 	 * If we can't access libzfs, don't bother doing anything.
942 	 */
943 	*zfs_libhandle = ((sa_handle_impl_t)handle)->zfs_libhandle;
944 	if (*zfs_libhandle == NULL)
945 		return (SA_SYSTEM_ERR);
946 
947 	*zfsgroup = find_or_create_group(handle, groupname, NULL, err);
948 	return (SA_OK);
949 }
950 
951 /*
952  * The O.G. zfs share preparation function. This initializes all zfs shares for
953  * use with libshare.
954  */
955 int
956 sa_get_zfs_shares(sa_handle_t handle, char *groupname)
957 {
958 	sa_group_t zfsgroup;
959 	zfs_handle_t **zlist;
960 	size_t count = 0;
961 	libzfs_handle_t *zfs_libhandle;
962 	int err;
963 
964 	if ((err = prep_zfs_handle_and_group(handle, groupname, &zfs_libhandle,
965 	    &zfsgroup, &err)) != SA_OK) {
966 		return (err);
967 	}
968 	/* Not an error, this could be a legacy condition */
969 	if (zfsgroup == NULL)
970 		return (SA_OK);
971 
972 	/*
973 	 * need to walk the mounted ZFS pools and datasets to
974 	 * find shares that are possible.
975 	 */
976 	get_all_filesystems((sa_handle_impl_t)handle, &zlist, &count);
977 	qsort(zlist, count, sizeof (void *), mountpoint_compare);
978 
979 	for (int i = 0; i < count; i++) {
980 		err = sa_get_zfs_share_common(handle, zlist[i], NULL, zfsgroup);
981 	}
982 	/*
983 	 * Don't need to free the "zlist" variable since it is only a
984 	 * pointer to a cached value that will be freed when
985 	 * sa_fini() is called.
986 	 */
987 	return (err);
988 }
989 
990 /*
991  * Initializes shares for only the dataset specified fs_handle.
992  * This is used as a performance optimization relative to sa_get_zfs_shares.
993  */
994 int
995 sa_get_zfs_share(sa_handle_t handle, char *groupname, zfs_handle_t *fs_handle)
996 {
997 	sa_group_t zfsgroup;
998 	libzfs_handle_t *zfs_libhandle;
999 	int err;
1000 
1001 	if ((err = prep_zfs_handle_and_group(handle, groupname, &zfs_libhandle,
1002 	    &zfsgroup, &err)) != SA_OK) {
1003 		return (err);
1004 	}
1005 	/* Not an error, this could be a legacy condition */
1006 	if (zfsgroup == NULL)
1007 		return (SA_OK);
1008 
1009 	err = sa_get_zfs_share_common(handle, fs_handle, NULL, zfsgroup);
1010 	return (err);
1011 }
1012 
1013 /*
1014  * Initializes only the handles specified in the sharearg for use with libshare.
1015  * This is used as a performance optimization relative to sa_get_zfs_shares.
1016  */
1017 int
1018 sa_get_one_zfs_share(sa_handle_t handle, char *groupname,
1019     sa_init_selective_arg_t *sharearg, char ***paths, size_t *paths_len)
1020 {
1021 	sa_group_t zfsgroup;
1022 	libzfs_handle_t *zfs_libhandle;
1023 	int err;
1024 
1025 	if ((err = prep_zfs_handle_and_group(handle, groupname, &zfs_libhandle,
1026 	    &zfsgroup, &err)) != SA_OK) {
1027 		return (err);
1028 	}
1029 	/* Not an error, this could be a legacy condition */
1030 	if (zfsgroup == NULL)
1031 		return (SA_OK);
1032 
1033 	*paths_len = sharearg->zhandle_len;
1034 	*paths = calloc(*paths_len, sizeof (char *));
1035 	for (int i = 0; i < sharearg->zhandle_len; ++i) {
1036 		zfs_handle_t *fs_handle =
1037 		    ((zfs_handle_t **)(sharearg->zhandle_arr))[i];
1038 		if (fs_handle == NULL) {
1039 			/* Free non-null elements of the paths array */
1040 			for (int free_idx = 0; free_idx < *paths_len;
1041 			    ++free_idx) {
1042 				if ((*paths)[free_idx] != NULL)
1043 					free((*paths)[free_idx]);
1044 			}
1045 			free(*paths);
1046 			*paths = NULL;
1047 			*paths_len = 0;
1048 			return (SA_SYSTEM_ERR);
1049 		}
1050 		(*paths)[i] = malloc(sizeof (char) * ZFS_MAXPROPLEN);
1051 		err |= sa_get_zfs_share_common(handle, fs_handle, (*paths)[i],
1052 		    zfsgroup);
1053 	}
1054 
1055 	return (err);
1056 }
1057 
1058 /*
1059  * Initializes only the share with the specified sharename for use with
1060  * libshare.
1061  */
1062 int
1063 sa_get_zfs_share_for_name(sa_handle_t handle, char *groupname,
1064     const char *sharename, char *outpath)
1065 {
1066 	sa_group_t zfsgroup;
1067 	libzfs_handle_t *zfs_libhandle;
1068 	int err;
1069 
1070 	if ((err = prep_zfs_handle_and_group(handle, groupname, &zfs_libhandle,
1071 	    &zfsgroup, &err)) != SA_OK) {
1072 		return (err);
1073 	}
1074 	/* Not an error, this could be a legacy condition */
1075 	if (zfsgroup == NULL)
1076 		return (SA_OK);
1077 
1078 	zfs_handle_t *fs_handle = zfs_open(zfs_libhandle,
1079 	    sharename + strspn(sharename, "/"), ZFS_TYPE_DATASET);
1080 	if (fs_handle == NULL)
1081 		return (SA_SYSTEM_ERR);
1082 
1083 	err = sa_get_zfs_share_common(handle, fs_handle, outpath, zfsgroup);
1084 	zfs_close(fs_handle);
1085 	return (err);
1086 }
1087 
1088 
1089 
1090 #define	COMMAND		"/usr/sbin/zfs"
1091 
1092 /*
1093  * sa_zfs_set_sharenfs(group, path, on)
1094  *
1095  * Update the "sharenfs" property on the path. If on is true, then set
1096  * to the properties on the group or "on" if no properties are
1097  * defined. Set to "off" if on is false.
1098  */
1099 
1100 int
1101 sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
1102 {
1103 	int ret = SA_NOT_IMPLEMENTED;
1104 	char *command;
1105 
1106 	command = malloc(ZFS_MAXPROPLEN * 2);
1107 	if (command != NULL) {
1108 		char *opts = NULL;
1109 		char *dataset = NULL;
1110 		FILE *pfile;
1111 		sa_handle_impl_t impl_handle;
1112 		/* for now, NFS is always available for "zfs" */
1113 		if (on) {
1114 			opts = sa_proto_legacy_format("nfs", group, 1);
1115 			if (opts != NULL && strlen(opts) == 0) {
1116 				free(opts);
1117 				opts = strdup("on");
1118 			}
1119 		}
1120 
1121 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1122 		assert(impl_handle != NULL);
1123 		if (impl_handle != NULL)
1124 			dataset = get_zfs_dataset(impl_handle, path, B_FALSE);
1125 		else
1126 			ret = SA_SYSTEM_ERR;
1127 
1128 		if (dataset != NULL) {
1129 			(void) snprintf(command, ZFS_MAXPROPLEN * 2,
1130 			    "%s set sharenfs=\"%s\" %s", COMMAND,
1131 			    opts != NULL ? opts : "off", dataset);
1132 			pfile = popen(command, "r");
1133 			if (pfile != NULL) {
1134 				ret = pclose(pfile);
1135 				if (ret != 0)
1136 					ret = SA_SYSTEM_ERR;
1137 			}
1138 		}
1139 		if (opts != NULL)
1140 			free(opts);
1141 		if (dataset != NULL)
1142 			free(dataset);
1143 		free(command);
1144 	}
1145 	return (ret);
1146 }
1147 
1148 /*
1149  * add_resources(share, opt)
1150  *
1151  * Add resource properties to those in "opt".  Resources are prefixed
1152  * with name=resourcename.
1153  */
1154 static char *
1155 add_resources(sa_share_t share, char *opt)
1156 {
1157 	char *newopt = NULL;
1158 	char *propstr;
1159 	sa_resource_t resource;
1160 
1161 	newopt = strdup(opt);
1162 	if (newopt == NULL)
1163 		return (newopt);
1164 
1165 	for (resource = sa_get_share_resource(share, NULL);
1166 	    resource != NULL;
1167 	    resource = sa_get_next_resource(resource)) {
1168 		char *name;
1169 		size_t size;
1170 
1171 		name = sa_get_resource_attr(resource, "name");
1172 		if (name == NULL) {
1173 			free(newopt);
1174 			return (NULL);
1175 		}
1176 		size = strlen(name) + strlen(opt) + sizeof ("name=") + 1;
1177 		newopt = calloc(1, size);
1178 		if (newopt != NULL)
1179 			(void) snprintf(newopt, size, "%s,name=%s", opt, name);
1180 		sa_free_attr_string(name);
1181 		free(opt);
1182 		opt = newopt;
1183 		propstr = sa_proto_legacy_format("smb", resource, 0);
1184 		if (propstr == NULL) {
1185 			free(opt);
1186 			return (NULL);
1187 		}
1188 		size = strlen(propstr) + strlen(opt) + 2;
1189 		newopt = calloc(1, size);
1190 		if (newopt != NULL)
1191 			(void) snprintf(newopt, size, "%s,%s", opt, propstr);
1192 		free(opt);
1193 		opt = newopt;
1194 	}
1195 	return (opt);
1196 }
1197 
1198 /*
1199  * sa_zfs_set_sharesmb(group, path, on)
1200  *
1201  * Update the "sharesmb" property on the path. If on is true, then set
1202  * to the properties on the group or "on" if no properties are
1203  * defined. Set to "off" if on is false.
1204  */
1205 
1206 int
1207 sa_zfs_set_sharesmb(sa_group_t group, char *path, int on)
1208 {
1209 	int ret = SA_NOT_IMPLEMENTED;
1210 	char *command;
1211 	sa_share_t share;
1212 
1213 	/* In case SMB not enabled */
1214 	if (sa_get_optionset(group, "smb") == NULL)
1215 		return (SA_NOT_SUPPORTED);
1216 
1217 	command = malloc(ZFS_MAXPROPLEN * 2);
1218 	if (command != NULL) {
1219 		char *opts = NULL;
1220 		char *dataset = NULL;
1221 		FILE *pfile;
1222 		sa_handle_impl_t impl_handle;
1223 
1224 		if (on) {
1225 			char *newopt;
1226 
1227 			share = sa_get_share(group, NULL);
1228 			opts = sa_proto_legacy_format("smb", share, 1);
1229 			if (opts != NULL && strlen(opts) == 0) {
1230 				free(opts);
1231 				opts = strdup("on");
1232 			}
1233 			newopt = add_resources(opts, share);
1234 			free(opts);
1235 			opts = newopt;
1236 		}
1237 
1238 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1239 		assert(impl_handle != NULL);
1240 		if (impl_handle != NULL)
1241 			dataset = get_zfs_dataset(impl_handle, path, B_FALSE);
1242 		else
1243 			ret = SA_SYSTEM_ERR;
1244 
1245 		if (dataset != NULL) {
1246 			(void) snprintf(command, ZFS_MAXPROPLEN * 2,
1247 			    "echo %s set sharesmb=\"%s\" %s", COMMAND,
1248 			    opts != NULL ? opts : "off", dataset);
1249 			pfile = popen(command, "r");
1250 			if (pfile != NULL) {
1251 				ret = pclose(pfile);
1252 				if (ret != 0)
1253 					ret = SA_SYSTEM_ERR;
1254 			}
1255 		}
1256 		if (opts != NULL)
1257 			free(opts);
1258 		if (dataset != NULL)
1259 			free(dataset);
1260 		free(command);
1261 	}
1262 	return (ret);
1263 }
1264 
1265 /*
1266  * sa_zfs_update(group)
1267  *
1268  * call back to ZFS to update the share if necessary.
1269  * Don't do it if it isn't a real change.
1270  */
1271 int
1272 sa_zfs_update(sa_group_t group)
1273 {
1274 	sa_optionset_t protopt;
1275 	sa_group_t parent;
1276 	char *command;
1277 	char *optstring;
1278 	int ret = SA_OK;
1279 	int doupdate = 0;
1280 	FILE *pfile;
1281 
1282 	if (sa_is_share(group))
1283 		parent = sa_get_parent_group(group);
1284 	else
1285 		parent = group;
1286 
1287 	if (parent != NULL) {
1288 		command = malloc(ZFS_MAXPROPLEN * 2);
1289 		if (command == NULL)
1290 			return (SA_NO_MEMORY);
1291 
1292 		*command = '\0';
1293 		for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
1294 		    protopt = sa_get_next_optionset(protopt)) {
1295 
1296 			char *proto = sa_get_optionset_attr(protopt, "type");
1297 			char *path;
1298 			char *dataset = NULL;
1299 			char *zfsopts = NULL;
1300 
1301 			if (sa_is_share(group)) {
1302 				path = sa_get_share_attr((sa_share_t)group,
1303 				    "path");
1304 				if (path != NULL) {
1305 					sa_handle_impl_t impl_handle;
1306 
1307 					impl_handle = sa_find_group_handle(
1308 					    group);
1309 					if (impl_handle != NULL)
1310 						dataset = get_zfs_dataset(
1311 						    impl_handle, path, B_FALSE);
1312 					else
1313 						ret = SA_SYSTEM_ERR;
1314 
1315 					sa_free_attr_string(path);
1316 				}
1317 			} else {
1318 				dataset = sa_get_group_attr(group, "name");
1319 			}
1320 			/* update only when there is an optstring found */
1321 			doupdate = 0;
1322 			if (proto != NULL && dataset != NULL) {
1323 				optstring = sa_proto_legacy_format(proto,
1324 				    group, 1);
1325 				zfsopts = get_zfs_property(dataset,
1326 				    ZFS_PROP_SHARENFS);
1327 
1328 				if (optstring != NULL && zfsopts != NULL) {
1329 					if (strcmp(optstring, zfsopts) != 0)
1330 						doupdate++;
1331 				}
1332 				if (doupdate) {
1333 					if (optstring != NULL &&
1334 					    strlen(optstring) > 0) {
1335 						(void) snprintf(command,
1336 						    ZFS_MAXPROPLEN * 2,
1337 						    "%s set share%s=%s %s",
1338 						    COMMAND, proto,
1339 						    optstring, dataset);
1340 					} else {
1341 						(void) snprintf(command,
1342 						    ZFS_MAXPROPLEN * 2,
1343 						    "%s set share%s=on %s",
1344 						    COMMAND, proto,
1345 						    dataset);
1346 					}
1347 					pfile = popen(command, "r");
1348 					if (pfile != NULL)
1349 						ret = pclose(pfile);
1350 					switch (ret) {
1351 					default:
1352 					case 1:
1353 						ret = SA_SYSTEM_ERR;
1354 						break;
1355 					case 2:
1356 						ret = SA_SYNTAX_ERR;
1357 						break;
1358 					case 0:
1359 						break;
1360 					}
1361 				}
1362 				if (optstring != NULL)
1363 					free(optstring);
1364 				if (zfsopts != NULL)
1365 					free(zfsopts);
1366 			}
1367 			if (proto != NULL)
1368 				sa_free_attr_string(proto);
1369 			if (dataset != NULL)
1370 				free(dataset);
1371 		}
1372 		free(command);
1373 	}
1374 	return (ret);
1375 }
1376 
1377 /*
1378  * sa_group_is_zfs(group)
1379  *
1380  * Given the group, determine if the zfs attribute is set.
1381  */
1382 
1383 int
1384 sa_group_is_zfs(sa_group_t group)
1385 {
1386 	char *zfs;
1387 	int ret = 0;
1388 
1389 	zfs = sa_get_group_attr(group, "zfs");
1390 	if (zfs != NULL) {
1391 		ret = 1;
1392 		sa_free_attr_string(zfs);
1393 	}
1394 	return (ret);
1395 }
1396 
1397 /*
1398  * sa_path_is_zfs(path)
1399  *
1400  * Check to see if the file system path represents is of type "zfs".
1401  */
1402 
1403 int
1404 sa_path_is_zfs(char *path)
1405 {
1406 	char *fstype;
1407 	int ret = 0;
1408 
1409 	fstype = sa_fstype(path);
1410 	if (fstype != NULL && strcmp(fstype, "zfs") == 0)
1411 		ret = 1;
1412 	if (fstype != NULL)
1413 		sa_free_fstype(fstype);
1414 	return (ret);
1415 }
1416 
1417 int
1418 sa_sharetab_fill_zfs(sa_share_t share, share_t *sh, char *proto)
1419 {
1420 	char *path;
1421 
1422 	/* Make sure path is valid */
1423 
1424 	path = sa_get_share_attr(share, "path");
1425 	if (path != NULL) {
1426 		(void) memset(sh, 0, sizeof (sh));
1427 		(void) sa_fillshare(share, proto, sh);
1428 		sa_free_attr_string(path);
1429 		return (0);
1430 	} else
1431 		return (1);
1432 }
1433 
1434 #define	SMAX(i, j)	\
1435 	if ((j) > (i)) { \
1436 		(i) = (j); \
1437 	}
1438 
1439 int
1440 sa_share_zfs(sa_share_t share, sa_resource_t resource, char *path, share_t *sh,
1441     void *exportdata, zfs_share_op_t operation)
1442 {
1443 	libzfs_handle_t *libhandle;
1444 	sa_group_t group;
1445 	sa_handle_t sahandle;
1446 	char *dataset;
1447 	int err = EINVAL;
1448 	int i, j;
1449 	char newpath[MAXPATHLEN];
1450 	char *pathp;
1451 
1452 	/*
1453 	 * First find the dataset name
1454 	 */
1455 	if ((group = sa_get_parent_group(share)) == NULL)  {
1456 		return (EINVAL);
1457 	}
1458 	if ((sahandle = sa_find_group_handle(group)) == NULL) {
1459 		return (EINVAL);
1460 	}
1461 
1462 	/*
1463 	 * If get_zfs_dataset fails, see if it is a subdirectory
1464 	 */
1465 
1466 	pathp = path;
1467 	while ((dataset = get_zfs_dataset(sahandle, pathp, B_TRUE)) == NULL) {
1468 		char *p;
1469 
1470 		if (pathp == path) {
1471 			(void) strlcpy(newpath, path, sizeof (newpath));
1472 			pathp = newpath;
1473 		}
1474 
1475 		/*
1476 		 * Make sure only one leading '/' This condition came
1477 		 * about when using HAStoragePlus which insisted on
1478 		 * putting an extra leading '/' in the ZFS path
1479 		 * name. The problem is fixed in other areas, but this
1480 		 * will catch any other ways that a double slash might
1481 		 * get introduced.
1482 		 */
1483 		while (*pathp == '/' && *(pathp + 1) == '/')
1484 			pathp++;
1485 
1486 		/*
1487 		 * chop off part of path, but if we are at root then
1488 		 * make sure path is a /
1489 		 */
1490 		if ((strlen(pathp) > 1) && (p = strrchr(pathp, '/'))) {
1491 			if (pathp == p) {
1492 				*(p + 1) = '\0';  /* skip over /, root case */
1493 			} else {
1494 				*p = '\0';
1495 			}
1496 		} else {
1497 			return (EINVAL);
1498 		}
1499 	}
1500 
1501 	libhandle = libzfs_init();
1502 	if (libhandle != NULL) {
1503 		char *resource_name;
1504 
1505 		i = (sh->sh_path ? strlen(sh->sh_path) : 0);
1506 		sh->sh_size = i;
1507 
1508 		j = (sh->sh_res ? strlen(sh->sh_res) : 0);
1509 		sh->sh_size += j;
1510 		SMAX(i, j);
1511 
1512 		j = (sh->sh_fstype ? strlen(sh->sh_fstype) : 0);
1513 		sh->sh_size += j;
1514 		SMAX(i, j);
1515 
1516 		j = (sh->sh_opts ? strlen(sh->sh_opts) : 0);
1517 		sh->sh_size += j;
1518 		SMAX(i, j);
1519 
1520 		j = (sh->sh_descr ? strlen(sh->sh_descr) : 0);
1521 		sh->sh_size += j;
1522 		SMAX(i, j);
1523 
1524 		resource_name = sa_get_resource_attr(resource, "name");
1525 
1526 		err = zfs_deleg_share_nfs(libhandle, dataset, path,
1527 		    resource_name, exportdata, sh, i, operation);
1528 		if (err == SA_OK)
1529 			sa_update_sharetab_ts(sahandle);
1530 		else
1531 			err = errno;
1532 		if (resource_name)
1533 			sa_free_attr_string(resource_name);
1534 
1535 		libzfs_fini(libhandle);
1536 	}
1537 	free(dataset);
1538 	return (err);
1539 }
1540 
1541 /*
1542  * sa_get_zfs_handle(handle)
1543  *
1544  * Given an sa_handle_t, return the libzfs_handle_t *. This is only
1545  * used internally by libzfs. Needed in order to avoid including
1546  * libshare_impl.h in libzfs.
1547  */
1548 
1549 libzfs_handle_t *
1550 sa_get_zfs_handle(sa_handle_t handle)
1551 {
1552 	sa_handle_impl_t implhandle = (sa_handle_impl_t)handle;
1553 
1554 	return (implhandle->zfs_libhandle);
1555 }
1556 
1557 /*
1558  * sa_get_zfs_info(libzfs, path, mountpoint, dataset)
1559  *
1560  * Find the ZFS dataset and mountpoint for a given path
1561  */
1562 int
1563 sa_zfs_get_info(libzfs_handle_t *libzfs, char *path, char *mountpointp,
1564     char *datasetp)
1565 {
1566 	get_all_cbdata_t cb = { 0 };
1567 	int i;
1568 	char mountpoint[ZFS_MAXPROPLEN];
1569 	char dataset[ZFS_MAXPROPLEN];
1570 	char canmount[ZFS_MAXPROPLEN];
1571 	char *dp;
1572 	int count;
1573 	int ret = 0;
1574 
1575 	cb.cb_types = ZFS_TYPE_FILESYSTEM;
1576 
1577 	if (libzfs == NULL)
1578 		return (0);
1579 
1580 	(void) zfs_iter_root(libzfs, get_one_filesystem, &cb);
1581 	count = cb.cb_used;
1582 
1583 	qsort(cb.cb_handles, count, sizeof (void *), mountpoint_compare);
1584 	for (i = 0; i < count; i++) {
1585 		/* must have a mountpoint */
1586 		if (zfs_prop_get(cb.cb_handles[i], ZFS_PROP_MOUNTPOINT,
1587 		    mountpoint, sizeof (mountpoint),
1588 		    NULL, NULL, 0, B_FALSE) != 0) {
1589 			/* no mountpoint */
1590 			continue;
1591 		}
1592 
1593 		/* mountpoint must be a path */
1594 		if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
1595 		    strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
1596 			/*
1597 			 * Search mmttab for mountpoint
1598 			 */
1599 
1600 			if (get_legacy_mountpoint(path, dataset,
1601 			    ZFS_MAXPROPLEN, mountpoint,
1602 			    ZFS_MAXPROPLEN) == 0) {
1603 				ret = 1;
1604 				break;
1605 			}
1606 			continue;
1607 		}
1608 
1609 		/* canmount must be set */
1610 		canmount[0] = '\0';
1611 		if (zfs_prop_get(cb.cb_handles[i], ZFS_PROP_CANMOUNT, canmount,
1612 		    sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 ||
1613 		    strcmp(canmount, "off") == 0)
1614 			continue;
1615 
1616 		/*
1617 		 * have a mountable handle but want to skip those marked none
1618 		 * and legacy
1619 		 */
1620 		if (strcmp(mountpoint, path) == 0) {
1621 			dp = (char *)zfs_get_name(cb.cb_handles[i]);
1622 			if (dp != NULL) {
1623 				if (datasetp != NULL)
1624 					(void) strcpy(datasetp, dp);
1625 				if (mountpointp != NULL)
1626 					(void) strcpy(mountpointp, mountpoint);
1627 				ret = 1;
1628 			}
1629 			break;
1630 		}
1631 
1632 	}
1633 
1634 	return (ret);
1635 }
1636 
1637 /*
1638  * This method builds values for "sharesmb" property from the
1639  * nvlist argument. The values are returned in sharesmb_val variable.
1640  */
1641 static int
1642 sa_zfs_sprintf_new_prop(nvlist_t *nvl, char *sharesmb_val)
1643 {
1644 	char cur_val[MAXPATHLEN];
1645 	char *name, *val;
1646 	nvpair_t *cur;
1647 	int err = 0;
1648 
1649 	cur = nvlist_next_nvpair(nvl, NULL);
1650 	while (cur != NULL) {
1651 		name = nvpair_name(cur);
1652 		err = nvpair_value_string(cur, &val);
1653 		if ((err != 0) || (name == NULL) || (val == NULL))
1654 			return (-1);
1655 
1656 		(void) snprintf(cur_val, MAXPATHLEN, "%s=%s,", name, val);
1657 		(void) strlcat(sharesmb_val, cur_val, MAXPATHLEN);
1658 
1659 		cur = nvlist_next_nvpair(nvl, cur);
1660 	}
1661 
1662 	return (0);
1663 }
1664 
1665 /*
1666  * This method builds values for "sharesmb" property from values
1667  * already existing on the share. The properties set via sa_zfs_sprint_new_prop
1668  * method are passed in sharesmb_val. If a existing property is already
1669  * set via sa_zfs_sprint_new_prop method, then they are not appended
1670  * to the sharesmb_val string. The returned sharesmb_val string is a combination
1671  * of new and existing values for 'sharesmb' property.
1672  */
1673 static int
1674 sa_zfs_sprintf_existing_prop(zfs_handle_t *handle, char *sharesmb_val)
1675 {
1676 	char shareopts[ZFS_MAXPROPLEN], cur_val[MAXPATHLEN];
1677 	char *token, *last, *value;
1678 
1679 	if (zfs_prop_get(handle, ZFS_PROP_SHARESMB, shareopts,
1680 	    sizeof (shareopts), NULL, NULL, 0, B_FALSE) != 0)
1681 		return (-1);
1682 
1683 	if (strstr(shareopts, "=") == NULL)
1684 		return (0);
1685 
1686 	for (token = strtok_r(shareopts, ",", &last); token != NULL;
1687 	    token = strtok_r(NULL, ",", &last)) {
1688 		value = strchr(token, '=');
1689 		if (value == NULL)
1690 			return (-1);
1691 		*value++ = '\0';
1692 
1693 		(void) snprintf(cur_val, MAXPATHLEN, "%s=", token);
1694 		if (strstr(sharesmb_val, cur_val) == NULL) {
1695 			(void) strlcat(cur_val, value, MAXPATHLEN);
1696 			(void) strlcat(cur_val, ",", MAXPATHLEN);
1697 			(void) strlcat(sharesmb_val, cur_val, MAXPATHLEN);
1698 		}
1699 	}
1700 
1701 	return (0);
1702 }
1703 
1704 /*
1705  * Sets the share properties on a ZFS share. For now, this method sets only
1706  * the "sharesmb" property.
1707  *
1708  * This method includes building a comma seperated name-value string to be
1709  * set on the "sharesmb" property of a ZFS share. This name-value string is
1710  * build in 2 steps:
1711  *    - New property values given as name-value pair are set first.
1712  *    - Existing optionset properties, which are not part of the new properties
1713  *	passed in step 1, are appended to the newly set properties.
1714  */
1715 int
1716 sa_zfs_setprop(sa_handle_t handle, char *path, nvlist_t *nvl)
1717 {
1718 	zfs_handle_t *z_fs;
1719 	libzfs_handle_t *z_lib;
1720 	char sharesmb_val[MAXPATHLEN];
1721 	char *dataset, *lastcomma;
1722 
1723 	if (nvlist_empty(nvl))
1724 		return (0);
1725 
1726 	if ((handle == NULL) || (path == NULL))
1727 		return (-1);
1728 
1729 	if ((dataset = get_zfs_dataset(handle, path, B_FALSE)) == NULL)
1730 		return (-1);
1731 
1732 	if ((z_lib = libzfs_init()) == NULL) {
1733 		free(dataset);
1734 		return (-1);
1735 	}
1736 
1737 	z_fs = zfs_open(z_lib, dataset, ZFS_TYPE_DATASET);
1738 	if (z_fs == NULL) {
1739 		free(dataset);
1740 		libzfs_fini(z_lib);
1741 		return (-1);
1742 	}
1743 
1744 	bzero(sharesmb_val, MAXPATHLEN);
1745 	if (sa_zfs_sprintf_new_prop(nvl, sharesmb_val) != 0) {
1746 		free(dataset);
1747 		zfs_close(z_fs);
1748 		libzfs_fini(z_lib);
1749 		return (-1);
1750 	}
1751 
1752 	if (sa_zfs_sprintf_existing_prop(z_fs, sharesmb_val) != 0) {
1753 		free(dataset);
1754 		zfs_close(z_fs);
1755 		libzfs_fini(z_lib);
1756 		return (-1);
1757 	}
1758 
1759 	lastcomma = strrchr(sharesmb_val, ',');
1760 	if ((lastcomma != NULL) && (lastcomma[1] == '\0'))
1761 		*lastcomma = '\0';
1762 
1763 	(void) zfs_prop_set(z_fs, zfs_prop_to_name(ZFS_PROP_SHARESMB),
1764 	    sharesmb_val);
1765 	free(dataset);
1766 	zfs_close(z_fs);
1767 	libzfs_fini(z_lib);
1768 
1769 	return (0);
1770 }
1771