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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdio.h>
28 #include <libzfs.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <libshare.h>
32 #include "libshare_impl.h"
33 #include <libintl.h>
34 #include <sys/mnttab.h>
35 #include <sys/mntent.h>
36 
37 extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *, uint64_t);
38 extern sa_group_t _sa_create_zfs_group(sa_group_t, char *);
39 extern char *sa_fstype(char *);
40 extern void set_node_attr(void *, char *, char *);
41 extern int sa_is_share(void *);
42 extern void sa_update_sharetab_ts(sa_handle_t);
43 
44 /*
45  * File system specific code for ZFS. The original code was stolen
46  * from the "zfs" command and modified to better suit this library's
47  * usage.
48  */
49 
50 typedef struct get_all_cbdata {
51 	zfs_handle_t	**cb_handles;
52 	size_t		cb_alloc;
53 	size_t		cb_used;
54 	uint_t		cb_types;
55 } get_all_cbdata_t;
56 
57 /*
58  * sa_zfs_init(impl_handle)
59  *
60  * Initialize an access handle into libzfs.  The handle needs to stay
61  * around until sa_zfs_fini() in order to maintain the cache of
62  * mounts.
63  */
64 
65 int
66 sa_zfs_init(sa_handle_impl_t impl_handle)
67 {
68 	impl_handle->zfs_libhandle = libzfs_init();
69 	if (impl_handle->zfs_libhandle != NULL) {
70 		libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE);
71 		return (B_TRUE);
72 	}
73 	return (B_FALSE);
74 }
75 
76 /*
77  * sa_zfs_fini(impl_handle)
78  *
79  * cleanup data structures and the libzfs handle used for accessing
80  * zfs file share info.
81  */
82 
83 void
84 sa_zfs_fini(sa_handle_impl_t impl_handle)
85 {
86 	if (impl_handle->zfs_libhandle != NULL) {
87 		if (impl_handle->zfs_list != NULL) {
88 			zfs_handle_t **zhp = impl_handle->zfs_list;
89 			size_t i;
90 
91 			/*
92 			 * Contents of zfs_list need to be freed so we
93 			 * don't lose ZFS handles.
94 			 */
95 			for (i = 0; i < impl_handle->zfs_list_count; i++) {
96 				zfs_close(zhp[i]);
97 			}
98 			free(impl_handle->zfs_list);
99 			impl_handle->zfs_list = NULL;
100 			impl_handle->zfs_list_count = 0;
101 		}
102 
103 		libzfs_fini(impl_handle->zfs_libhandle);
104 		impl_handle->zfs_libhandle = NULL;
105 	}
106 }
107 
108 /*
109  * get_one_filesystem(zfs_handle_t, data)
110  *
111  * an iterator function called while iterating through the ZFS
112  * root. It accumulates into an array of file system handles that can
113  * be used to derive info about those file systems.
114  *
115  * Note that as this function is called, we close all zhp handles that
116  * are not going to be places into the cp_handles list. We don't want
117  * to close the ones we are keeping, but all others would be leaked if
118  * not closed here.
119  */
120 
121 static int
122 get_one_filesystem(zfs_handle_t *zhp, void *data)
123 {
124 	get_all_cbdata_t *cbp = data;
125 	zfs_type_t type = zfs_get_type(zhp);
126 
127 	/*
128 	 * Interate over any nested datasets.
129 	 */
130 	if (type == ZFS_TYPE_FILESYSTEM &&
131 	    zfs_iter_filesystems(zhp, get_one_filesystem, data) != 0) {
132 		zfs_close(zhp);
133 		return (1);
134 	}
135 
136 	/*
137 	 * Skip any datasets whose type does not match.
138 	 */
139 	if ((type & cbp->cb_types) == 0) {
140 		zfs_close(zhp);
141 		return (0);
142 	}
143 
144 	if (cbp->cb_alloc == cbp->cb_used) {
145 		zfs_handle_t **handles;
146 
147 		if (cbp->cb_alloc == 0)
148 			cbp->cb_alloc = 64;
149 		else
150 			cbp->cb_alloc *= 2;
151 
152 		handles = (zfs_handle_t **)calloc(1,
153 		    cbp->cb_alloc * sizeof (void *));
154 
155 		if (handles == NULL) {
156 			zfs_close(zhp);
157 			return (0);
158 		}
159 		if (cbp->cb_handles) {
160 			bcopy(cbp->cb_handles, handles,
161 			    cbp->cb_used * sizeof (void *));
162 			free(cbp->cb_handles);
163 		}
164 
165 		cbp->cb_handles = handles;
166 	}
167 
168 	cbp->cb_handles[cbp->cb_used++] = zhp;
169 
170 	return (0);
171 }
172 
173 /*
174  * get_all_filesystems(zfs_handle_t ***fslist, size_t *count)
175  *
176  * iterate through all ZFS file systems starting at the root. Returns
177  * a count and an array of handle pointers. Allocating is only done
178  * once. The caller does not need to free since it will be done at
179  * sa_zfs_fini() time.
180  */
181 
182 static void
183 get_all_filesystems(sa_handle_impl_t impl_handle,
184 			zfs_handle_t ***fslist, size_t *count)
185 {
186 	get_all_cbdata_t cb = { 0 };
187 	cb.cb_types = ZFS_TYPE_FILESYSTEM;
188 
189 	if (impl_handle->zfs_list != NULL) {
190 		*fslist = impl_handle->zfs_list;
191 		*count = impl_handle->zfs_list_count;
192 		return;
193 	}
194 
195 	(void) zfs_iter_root(impl_handle->zfs_libhandle,
196 	    get_one_filesystem, &cb);
197 
198 	impl_handle->zfs_list = *fslist = cb.cb_handles;
199 	impl_handle->zfs_list_count = *count = cb.cb_used;
200 }
201 
202 /*
203  * mountpoint_compare(a, b)
204  *
205  * compares the mountpoint on two zfs file systems handles.
206  * returns values following strcmp() model.
207  */
208 
209 static int
210 mountpoint_compare(const void *a, const void *b)
211 {
212 	zfs_handle_t **za = (zfs_handle_t **)a;
213 	zfs_handle_t **zb = (zfs_handle_t **)b;
214 	char mounta[MAXPATHLEN];
215 	char mountb[MAXPATHLEN];
216 
217 	verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
218 	    sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
219 	verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
220 	    sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
221 
222 	return (strcmp(mounta, mountb));
223 }
224 
225 /*
226  * return legacy mountpoint.  Caller provides space for mountpoint.
227  */
228 int
229 get_legacy_mountpoint(char *path, char *mountpoint, size_t len)
230 {
231 	FILE *fp;
232 	struct mnttab entry;
233 
234 	if ((fp = fopen(MNTTAB, "r")) == NULL) {
235 		return (1);
236 	}
237 
238 	while (getmntent(fp, &entry) == 0) {
239 
240 		if (entry.mnt_fstype == NULL ||
241 		    strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
242 			continue;
243 
244 		if (strcmp(entry.mnt_mountp, path) == 0) {
245 			(void) strlcpy(mountpoint, entry.mnt_special, len);
246 			(void) fclose(fp);
247 			return (0);
248 		}
249 	}
250 	(void) fclose(fp);
251 	return (1);
252 }
253 
254 /*
255  * get_zfs_dataset(impl_handle, path)
256  *
257  * get the name of the ZFS dataset the path is equivalent to.  The
258  * dataset name is used for get/set of ZFS properties since libzfs
259  * requires a dataset to do a zfs_open().
260  */
261 
262 static char *
263 get_zfs_dataset(sa_handle_impl_t impl_handle, char *path,
264     boolean_t search_mnttab)
265 {
266 	size_t i, count = 0;
267 	char *dataset = NULL;
268 	zfs_handle_t **zlist;
269 	char mountpoint[ZFS_MAXPROPLEN];
270 	char canmount[ZFS_MAXPROPLEN];
271 
272 	get_all_filesystems(impl_handle, &zlist, &count);
273 	qsort(zlist, count, sizeof (void *), mountpoint_compare);
274 	for (i = 0; i < count; i++) {
275 		/* must have a mountpoint */
276 		if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint,
277 		    sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
278 			/* no mountpoint */
279 			continue;
280 		}
281 
282 		/* mountpoint must be a path */
283 		if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
284 		    strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
285 			/*
286 			 * Search mmttab for mountpoint
287 			 */
288 
289 			if (search_mnttab == B_TRUE &&
290 			    get_legacy_mountpoint(path, mountpoint,
291 			    sizeof (mountpoint)) == 0) {
292 				dataset = mountpoint;
293 				break;
294 			}
295 			continue;
296 		}
297 
298 		/* canmount must be set */
299 		canmount[0] = '\0';
300 		if (zfs_prop_get(zlist[i], ZFS_PROP_CANMOUNT, canmount,
301 		    sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 ||
302 		    strcmp(canmount, "off") == 0)
303 			continue;
304 
305 		/*
306 		 * have a mountable handle but want to skip those marked none
307 		 * and legacy
308 		 */
309 		if (strcmp(mountpoint, path) == 0) {
310 			dataset = (char *)zfs_get_name(zlist[i]);
311 			break;
312 		}
313 
314 	}
315 
316 	if (dataset != NULL)
317 		dataset = strdup(dataset);
318 
319 	return (dataset);
320 }
321 
322 /*
323  * get_zfs_property(dataset, property)
324  *
325  * Get the file system property specified from the ZFS dataset.
326  */
327 
328 static char *
329 get_zfs_property(char *dataset, zfs_prop_t property)
330 {
331 	zfs_handle_t *handle = NULL;
332 	char shareopts[ZFS_MAXPROPLEN];
333 	libzfs_handle_t *libhandle;
334 
335 	libhandle = libzfs_init();
336 	if (libhandle != NULL) {
337 		handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
338 		if (handle != NULL) {
339 			if (zfs_prop_get(handle, property, shareopts,
340 			    sizeof (shareopts), NULL, NULL, 0,
341 			    B_FALSE) == 0) {
342 				zfs_close(handle);
343 				libzfs_fini(libhandle);
344 				return (strdup(shareopts));
345 			}
346 			zfs_close(handle);
347 		}
348 		libzfs_fini(libhandle);
349 	}
350 	return (NULL);
351 }
352 
353 /*
354  * sa_zfs_is_shared(handle, path)
355  *
356  * Check to see if the ZFS path provided has the sharenfs option set
357  * or not.
358  */
359 
360 int
361 sa_zfs_is_shared(sa_handle_t sahandle, char *path)
362 {
363 	int ret = 0;
364 	char *dataset;
365 	zfs_handle_t *handle = NULL;
366 	char shareopts[ZFS_MAXPROPLEN];
367 	libzfs_handle_t *libhandle;
368 
369 	dataset = get_zfs_dataset((sa_handle_t)sahandle, path, B_FALSE);
370 	if (dataset != NULL) {
371 		libhandle = libzfs_init();
372 		if (libhandle != NULL) {
373 			handle = zfs_open(libhandle, dataset,
374 			    ZFS_TYPE_FILESYSTEM);
375 			if (handle != NULL) {
376 				if (zfs_prop_get(handle, ZFS_PROP_SHARENFS,
377 				    shareopts, sizeof (shareopts), NULL, NULL,
378 				    0, B_FALSE) == 0 &&
379 				    strcmp(shareopts, "off") != 0) {
380 					ret = 1; /* it is shared */
381 				}
382 				zfs_close(handle);
383 			}
384 			libzfs_fini(libhandle);
385 		}
386 		free(dataset);
387 	}
388 	return (ret);
389 }
390 
391 /*
392  * find_or_create_group(handle, groupname, proto, *err)
393  *
394  * While walking the ZFS tree, we need to add shares to a defined
395  * group. If the group doesn't exist, create it first, making sure it
396  * is marked as a ZFS group.
397  *
398  * Note that all ZFS shares are in a subgroup of the top level group
399  * called "zfs".
400  */
401 
402 static sa_group_t
403 find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err)
404 {
405 	sa_group_t group;
406 	sa_optionset_t optionset;
407 	int ret = SA_OK;
408 
409 	/*
410 	 * we check to see if the "zfs" group exists. Since this
411 	 * should be the top level group, we don't want the
412 	 * parent. This is to make sure the zfs group has been created
413 	 * and to created if it hasn't been.
414 	 */
415 	group = sa_get_group(handle, groupname);
416 	if (group == NULL) {
417 		group = sa_create_group(handle, groupname, &ret);
418 
419 		/* make sure this is flagged as a ZFS group */
420 		if (group != NULL)
421 			ret = sa_set_group_attr(group, "zfs", "true");
422 	}
423 	if (group != NULL) {
424 		if (proto != NULL) {
425 			optionset = sa_get_optionset(group, proto);
426 			if (optionset == NULL)
427 				optionset = sa_create_optionset(group, proto);
428 		}
429 	}
430 	if (err != NULL)
431 		*err = ret;
432 	return (group);
433 }
434 
435 /*
436  * find_or_create_zfs_subgroup(groupname, optstring, *err)
437  *
438  * ZFS shares will be in a subgroup of the "zfs" master group.  This
439  * function looks to see if the groupname exists and returns it if it
440  * does or else creates a new one with the specified name and returns
441  * that.  The "zfs" group will exist before we get here, but we make
442  * sure just in case.
443  *
444  * err must be a valid pointer.
445  */
446 
447 static sa_group_t
448 find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname, char *proto,
449     char *optstring, int *err)
450 {
451 	sa_group_t group = NULL;
452 	sa_group_t zfs;
453 	char *name;
454 	char *options;
455 
456 	/* start with the top-level "zfs" group */
457 	zfs = sa_get_group(handle, "zfs");
458 	*err = SA_OK;
459 	if (zfs != NULL) {
460 		for (group = sa_get_sub_group(zfs); group != NULL;
461 		    group = sa_get_next_group(group)) {
462 			name = sa_get_group_attr(group, "name");
463 			if (name != NULL && strcmp(name, groupname) == 0) {
464 				/* have the group so break out of here */
465 				sa_free_attr_string(name);
466 				break;
467 			}
468 			if (name != NULL)
469 				sa_free_attr_string(name);
470 		}
471 
472 		if (group == NULL) {
473 			/*
474 			 * Need to create the sub-group since it doesn't exist
475 			 */
476 			group = _sa_create_zfs_group(zfs, groupname);
477 			if (group == NULL) {
478 				*err = SA_NO_MEMORY;
479 				return (NULL);
480 			}
481 			set_node_attr(group, "zfs", "true");
482 		}
483 		if (strcmp(optstring, "on") == 0)
484 			optstring = "rw";
485 		options = strdup(optstring);
486 		if (options != NULL) {
487 			*err = sa_parse_legacy_options(group, options,
488 			    proto);
489 			/* If no optionset, add one. */
490 			if (sa_get_optionset(group, proto) == NULL)
491 				(void) sa_create_optionset(group, proto);
492 			free(options);
493 		} else {
494 			*err = SA_NO_MEMORY;
495 		}
496 	}
497 	return (group);
498 }
499 
500 /*
501  * zfs_construct_resource(share, name, base, dataset)
502  *
503  * Add a resource to the share using name as a template. If name ==
504  * NULL, then construct a name based on the dataset value.
505  * name.
506  */
507 static void
508 zfs_construct_resource(sa_share_t share, char *dataset)
509 {
510 	char buff[SA_MAX_RESOURCE_NAME + 1];
511 	int ret = SA_OK;
512 
513 	(void) snprintf(buff, SA_MAX_RESOURCE_NAME, "%s", dataset);
514 	sa_fix_resource_name(buff);
515 	(void) sa_add_resource(share, buff, SA_SHARE_TRANSIENT, &ret);
516 }
517 
518 /*
519  * zfs_inherited(handle, source, sourcestr)
520  *
521  * handle case of inherited share{nfs,smb}. Pulled out of sa_get_zfs_shares
522  * for readability.
523  */
524 static int
525 zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr,
526     char *shareopts, char *mountpoint, char *proto, char *dataset)
527 {
528 	int doshopt = 0;
529 	int err = SA_OK;
530 	sa_group_t group;
531 	sa_resource_t resource;
532 	uint64_t features;
533 
534 	/*
535 	 * Need to find the "real" parent sub-group. It may not be
536 	 * mounted, but it was identified in the "sourcestr"
537 	 * variable. The real parent not mounted can occur if
538 	 * "canmount=off and sharenfs=on".
539 	 */
540 	group = find_or_create_zfs_subgroup(handle, sourcestr, proto,
541 	    shareopts, &doshopt);
542 	if (group != NULL) {
543 		/*
544 		 * We may need the first share for resource
545 		 * prototype. We only care about it if it has a
546 		 * resource that sets a prefix value.
547 		 */
548 		if (share == NULL)
549 			share = _sa_add_share(group, mountpoint,
550 			    SA_SHARE_TRANSIENT, &err,
551 			    (uint64_t)SA_FEATURE_NONE);
552 		/*
553 		 * some options may only be on shares. If the opt
554 		 * string contains one of those, we put it just on the
555 		 * share.
556 		 */
557 		if (share != NULL && doshopt == SA_PROP_SHARE_ONLY) {
558 			char *options;
559 			options = strdup(shareopts);
560 			if (options != NULL) {
561 				set_node_attr(share, "dataset", dataset);
562 				err = sa_parse_legacy_options(share, options,
563 				    proto);
564 				set_node_attr(share, "dataset", NULL);
565 				free(options);
566 			}
567 			if (sa_get_optionset(group, proto) == NULL)
568 				(void) sa_create_optionset(group, proto);
569 		}
570 		features = sa_proto_get_featureset(proto);
571 		if (share != NULL && features & SA_FEATURE_RESOURCE) {
572 			/*
573 			 * We have a share and the protocol requires
574 			 * that at least one resource exist (probably
575 			 * SMB). We need to make sure that there is at
576 			 * least one.
577 			 */
578 			resource = sa_get_share_resource(share, NULL);
579 			if (resource == NULL) {
580 				zfs_construct_resource(share, dataset);
581 			}
582 		}
583 	} else {
584 		err = SA_NO_MEMORY;
585 	}
586 	return (err);
587 }
588 
589 /*
590  * zfs_notinherited(group, share, mountpoint, shareopts, proto, dataset,
591  *     grouperr)
592  *
593  * handle case where this is the top of a sub-group in ZFS. Pulled out
594  * of sa_get_zfs_shares for readability. We need the grouperr from the
595  * creation of the subgroup to know whether to add the public
596  * property, etc. to the specific share.
597  */
598 static int
599 zfs_notinherited(sa_group_t group, sa_share_t share, char *mountpoint,
600     char *shareopts, char *proto, char *dataset, int grouperr)
601 {
602 	int err = SA_OK;
603 	sa_resource_t resource;
604 	uint64_t features;
605 
606 	set_node_attr(group, "zfs", "true");
607 	if (share == NULL)
608 		share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT,
609 		    &err, (uint64_t)SA_FEATURE_NONE);
610 
611 	if (err != SA_OK)
612 		return (err);
613 
614 	if (strcmp(shareopts, "on") == 0)
615 		shareopts = "";
616 	if (shareopts != NULL) {
617 		char *options;
618 		if (grouperr == SA_PROP_SHARE_ONLY) {
619 			/*
620 			 * Some properties may only be on shares, but
621 			 * due to the ZFS sub-groups being artificial,
622 			 * we sometimes get this and have to deal with
623 			 * it. We do it by attempting to put it on the
624 			 * share.
625 			 */
626 			options = strdup(shareopts);
627 			if (options != NULL) {
628 				err = sa_parse_legacy_options(share,
629 				    options, proto);
630 				free(options);
631 			}
632 		}
633 		/* Unmark the share's changed state */
634 		set_node_attr(share, "changed", NULL);
635 	}
636 	features = sa_proto_get_featureset(proto);
637 	if (share != NULL && features & SA_FEATURE_RESOURCE) {
638 		/*
639 		 * We have a share and the protocol requires that at
640 		 * least one resource exist (probably SMB). We need to
641 		 * make sure that there is at least one.
642 		 */
643 		resource = sa_get_share_resource(share, NULL);
644 		if (resource == NULL) {
645 			zfs_construct_resource(share, dataset);
646 		}
647 	}
648 	return (err);
649 }
650 
651 /*
652  * zfs_grp_error(err)
653  *
654  * Print group create error, but only once. If err is 0 do the
655  * print else don't.
656  */
657 
658 static void
659 zfs_grp_error(int err)
660 {
661 	if (err == 0) {
662 		/* only print error once */
663 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
664 		    "Cannot create ZFS subgroup during initialization:"
665 		    " %s\n"), sa_errorstr(SA_SYSTEM_ERR));
666 	}
667 }
668 
669 /*
670  * zfs_process_share(handle, share, mountpoint, proto, source,
671  *     shareopts, sourcestr)
672  *
673  * Creates the subgroup, if necessary and adds shares, resources
674  * and properties.
675  */
676 int
677 sa_zfs_process_share(sa_handle_t handle, sa_group_t group, sa_share_t share,
678     char *mountpoint, char *proto, zprop_source_t source, char *shareopts,
679     char *sourcestr, char *dataset)
680 {
681 	int err = SA_OK;
682 
683 	if (source & ZPROP_SRC_INHERITED) {
684 		err = zfs_inherited(handle, share, sourcestr, shareopts,
685 		    mountpoint, proto, dataset);
686 	} else {
687 		group = find_or_create_zfs_subgroup(handle, dataset, proto,
688 		    shareopts, &err);
689 		if (group == NULL) {
690 			static boolean_t reported_error = B_FALSE;
691 			/*
692 			 * There is a problem, but we can't do
693 			 * anything about it at this point so we issue
694 			 * a warning and move on.
695 			 */
696 			zfs_grp_error(reported_error);
697 			reported_error = B_TRUE;
698 		}
699 		set_node_attr(group, "zfs", "true");
700 		/*
701 		 * Add share with local opts via zfs_notinherited.
702 		 */
703 		err = zfs_notinherited(group, share, mountpoint, shareopts,
704 		    proto, dataset, err);
705 	}
706 	return (err);
707 }
708 
709 /*
710  * sa_get_zfs_shares(handle, groupname)
711  *
712  * Walk the mnttab for all zfs mounts and determine which are
713  * shared. Find or create the appropriate group/sub-group to contain
714  * the shares.
715  *
716  * All shares are in a sub-group that will hold the properties. This
717  * allows representing the inherited property model.
718  *
719  * One area of complication is if "sharenfs" is set at one level of
720  * the directory tree and "sharesmb" is set at a different level, the
721  * a sub-group must be formed at the lower level for both
722  * protocols. That is the nature of the problem in CR 6667349.
723  */
724 
725 int
726 sa_get_zfs_shares(sa_handle_t handle, char *groupname)
727 {
728 	sa_group_t zfsgroup;
729 	boolean_t nfs;
730 	boolean_t nfs_inherited;
731 	boolean_t smb;
732 	boolean_t smb_inherited;
733 	zfs_handle_t **zlist;
734 	char nfsshareopts[ZFS_MAXPROPLEN];
735 	char smbshareopts[ZFS_MAXPROPLEN];
736 	sa_share_t share;
737 	zprop_source_t source;
738 	char nfssourcestr[ZFS_MAXPROPLEN];
739 	char smbsourcestr[ZFS_MAXPROPLEN];
740 	char mountpoint[ZFS_MAXPROPLEN];
741 	size_t count = 0, i;
742 	libzfs_handle_t *zfs_libhandle;
743 	int err = SA_OK;
744 
745 	/*
746 	 * If we can't access libzfs, don't bother doing anything.
747 	 */
748 	zfs_libhandle = ((sa_handle_impl_t)handle)->zfs_libhandle;
749 	if (zfs_libhandle == NULL)
750 		return (SA_SYSTEM_ERR);
751 
752 	zfsgroup = find_or_create_group(handle, groupname, NULL, &err);
753 	/* Not an error, this could be a legacy condition */
754 	if (zfsgroup == NULL)
755 		return (SA_OK);
756 
757 	/*
758 	 * need to walk the mounted ZFS pools and datasets to
759 	 * find shares that are possible.
760 	 */
761 	get_all_filesystems((sa_handle_impl_t)handle, &zlist, &count);
762 	qsort(zlist, count, sizeof (void *), mountpoint_compare);
763 
764 	for (i = 0; i < count; i++) {
765 		char *dataset;
766 
767 		source = ZPROP_SRC_ALL;
768 		/* If no mountpoint, skip. */
769 		if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT,
770 		    mountpoint, sizeof (mountpoint), NULL, NULL, 0,
771 		    B_FALSE) != 0)
772 			continue;
773 
774 		/*
775 		 * zfs_get_name value must not be freed. It is just a
776 		 * pointer to a value in the handle.
777 		 */
778 		if ((dataset = (char *)zfs_get_name(zlist[i])) == NULL)
779 			continue;
780 
781 		/*
782 		 * only deal with "mounted" file systems since
783 		 * unmounted file systems can't actually be shared.
784 		 */
785 
786 		if (!zfs_is_mounted(zlist[i], NULL))
787 			continue;
788 
789 		nfs = nfs_inherited = B_FALSE;
790 
791 		if (zfs_prop_get(zlist[i], ZFS_PROP_SHARENFS, nfsshareopts,
792 		    sizeof (nfsshareopts), &source, nfssourcestr,
793 		    ZFS_MAXPROPLEN, B_FALSE) == 0 &&
794 		    strcmp(nfsshareopts, "off") != 0) {
795 			if (source & ZPROP_SRC_INHERITED)
796 				nfs_inherited = B_TRUE;
797 			else
798 				nfs = B_TRUE;
799 		}
800 
801 		smb = smb_inherited = B_FALSE;
802 		if (zfs_prop_get(zlist[i], ZFS_PROP_SHARESMB, smbshareopts,
803 		    sizeof (smbshareopts), &source, smbsourcestr,
804 		    ZFS_MAXPROPLEN, B_FALSE) == 0 &&
805 		    strcmp(smbshareopts, "off") != 0) {
806 			if (source & ZPROP_SRC_INHERITED)
807 				smb_inherited = B_TRUE;
808 			else
809 				smb = B_TRUE;
810 		}
811 
812 		/*
813 		 * If the mountpoint is already shared, it must be a
814 		 * non-ZFS share. We want to remove the share from its
815 		 * parent group and reshare it under ZFS.
816 		 */
817 		share = sa_find_share(handle, mountpoint);
818 		if (share != NULL &&
819 		    (nfs || smb || nfs_inherited || smb_inherited)) {
820 			err = sa_remove_share(share);
821 			share = NULL;
822 		}
823 
824 		/*
825 		 * At this point, we have the information needed to
826 		 * determine what to do with the share.
827 		 *
828 		 * If smb or nfs is set, we have a new sub-group.
829 		 * If smb_inherit and/or nfs_inherit is set, then
830 		 * place on an existing sub-group. If both are set,
831 		 * the existing sub-group is the closest up the tree.
832 		 */
833 		if (nfs || smb) {
834 			/*
835 			 * Non-inherited is the straightforward
836 			 * case. sa_zfs_process_share handles it
837 			 * directly. Make sure that if the "other"
838 			 * protocol is inherited, that we treat it as
839 			 * non-inherited as well.
840 			 */
841 			if (nfs || nfs_inherited) {
842 				err = sa_zfs_process_share(handle, zfsgroup,
843 				    share, mountpoint, "nfs",
844 				    0, nfsshareopts,
845 				    nfssourcestr, dataset);
846 				share = sa_find_share(handle, mountpoint);
847 			}
848 			if (smb || smb_inherited) {
849 				err = sa_zfs_process_share(handle, zfsgroup,
850 				    share, mountpoint, "smb",
851 				    0, smbshareopts,
852 				    smbsourcestr, dataset);
853 			}
854 		} else if (nfs_inherited || smb_inherited) {
855 			char *grpdataset;
856 			/*
857 			 * If we only have inherited groups, it is
858 			 * important to find the closer of the two if
859 			 * the protocols are set at different
860 			 * levels. The closest sub-group is the one we
861 			 * want to work with.
862 			 */
863 			if (nfs_inherited && smb_inherited) {
864 				if (strcmp(nfssourcestr, smbsourcestr) <= 0)
865 					grpdataset = nfssourcestr;
866 				else
867 					grpdataset = smbsourcestr;
868 			} else if (nfs_inherited) {
869 				grpdataset = nfssourcestr;
870 			} else if (smb_inherited) {
871 				grpdataset = smbsourcestr;
872 			}
873 			if (nfs_inherited) {
874 				err = sa_zfs_process_share(handle, zfsgroup,
875 				    share, mountpoint, "nfs",
876 				    ZPROP_SRC_INHERITED, nfsshareopts,
877 				    grpdataset, dataset);
878 				share = sa_find_share(handle, mountpoint);
879 			}
880 			if (smb_inherited) {
881 				err = sa_zfs_process_share(handle, zfsgroup,
882 				    share, mountpoint, "smb",
883 				    ZPROP_SRC_INHERITED, smbshareopts,
884 				    grpdataset, dataset);
885 			}
886 		}
887 	}
888 	/*
889 	 * Don't need to free the "zlist" variable since it is only a
890 	 * pointer to a cached value that will be freed when
891 	 * sa_fini() is called.
892 	 */
893 	return (err);
894 }
895 
896 #define	COMMAND		"/usr/sbin/zfs"
897 
898 /*
899  * sa_zfs_set_sharenfs(group, path, on)
900  *
901  * Update the "sharenfs" property on the path. If on is true, then set
902  * to the properties on the group or "on" if no properties are
903  * defined. Set to "off" if on is false.
904  */
905 
906 int
907 sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
908 {
909 	int ret = SA_NOT_IMPLEMENTED;
910 	char *command;
911 
912 	command = malloc(ZFS_MAXPROPLEN * 2);
913 	if (command != NULL) {
914 		char *opts = NULL;
915 		char *dataset = NULL;
916 		FILE *pfile;
917 		sa_handle_impl_t impl_handle;
918 		/* for now, NFS is always available for "zfs" */
919 		if (on) {
920 			opts = sa_proto_legacy_format("nfs", group, 1);
921 			if (opts != NULL && strlen(opts) == 0) {
922 				free(opts);
923 				opts = strdup("on");
924 			}
925 		}
926 
927 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
928 		assert(impl_handle != NULL);
929 		if (impl_handle != NULL)
930 			dataset = get_zfs_dataset(impl_handle, path, B_FALSE);
931 		else
932 			ret = SA_SYSTEM_ERR;
933 
934 		if (dataset != NULL) {
935 			(void) snprintf(command, ZFS_MAXPROPLEN * 2,
936 			    "%s set sharenfs=\"%s\" %s", COMMAND,
937 			    opts != NULL ? opts : "off", dataset);
938 			pfile = popen(command, "r");
939 			if (pfile != NULL) {
940 				ret = pclose(pfile);
941 				if (ret != 0)
942 					ret = SA_SYSTEM_ERR;
943 			}
944 		}
945 		if (opts != NULL)
946 			free(opts);
947 		if (dataset != NULL)
948 			free(dataset);
949 		free(command);
950 	}
951 	return (ret);
952 }
953 
954 /*
955  * add_resources(share, opt)
956  *
957  * Add resource properties to those in "opt".  Resources are prefixed
958  * with name=resourcename.
959  */
960 static char *
961 add_resources(sa_share_t share, char *opt)
962 {
963 	char *newopt = NULL;
964 	char *propstr;
965 	sa_resource_t resource;
966 
967 	newopt = strdup(opt);
968 	if (newopt == NULL)
969 		return (newopt);
970 
971 	for (resource = sa_get_share_resource(share, NULL);
972 	    resource != NULL;
973 	    resource = sa_get_next_resource(resource)) {
974 		char *name;
975 		size_t size;
976 
977 		name = sa_get_resource_attr(resource, "name");
978 		if (name == NULL) {
979 			free(newopt);
980 			return (NULL);
981 		}
982 		size = strlen(name) + strlen(opt) + sizeof ("name=") + 1;
983 		newopt = calloc(1, size);
984 		if (newopt != NULL)
985 			(void) snprintf(newopt, size, "%s,name=%s", opt, name);
986 		free(opt);
987 		opt = newopt;
988 		propstr = sa_proto_legacy_format("smb", resource, 0);
989 		if (propstr == NULL) {
990 			free(opt);
991 			return (NULL);
992 		}
993 		size = strlen(propstr) + strlen(opt) + 2;
994 		newopt = calloc(1, size);
995 		if (newopt != NULL)
996 			(void) snprintf(newopt, size, "%s,%s", opt, propstr);
997 		free(opt);
998 		opt = newopt;
999 	}
1000 	return (opt);
1001 }
1002 
1003 /*
1004  * sa_zfs_set_sharesmb(group, path, on)
1005  *
1006  * Update the "sharesmb" property on the path. If on is true, then set
1007  * to the properties on the group or "on" if no properties are
1008  * defined. Set to "off" if on is false.
1009  */
1010 
1011 int
1012 sa_zfs_set_sharesmb(sa_group_t group, char *path, int on)
1013 {
1014 	int ret = SA_NOT_IMPLEMENTED;
1015 	char *command;
1016 	sa_share_t share;
1017 
1018 	/* In case SMB not enabled */
1019 	if (sa_get_optionset(group, "smb") == NULL)
1020 		return (SA_NOT_SUPPORTED);
1021 
1022 	command = malloc(ZFS_MAXPROPLEN * 2);
1023 	if (command != NULL) {
1024 		char *opts = NULL;
1025 		char *dataset = NULL;
1026 		FILE *pfile;
1027 		sa_handle_impl_t impl_handle;
1028 
1029 		if (on) {
1030 			char *newopt;
1031 
1032 			share = sa_get_share(group, NULL);
1033 			opts = sa_proto_legacy_format("smb", share, 1);
1034 			if (opts != NULL && strlen(opts) == 0) {
1035 				free(opts);
1036 				opts = strdup("on");
1037 			}
1038 			newopt = add_resources(opts, share);
1039 			free(opts);
1040 			opts = newopt;
1041 		}
1042 
1043 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1044 		assert(impl_handle != NULL);
1045 		if (impl_handle != NULL)
1046 			dataset = get_zfs_dataset(impl_handle, path, B_FALSE);
1047 		else
1048 			ret = SA_SYSTEM_ERR;
1049 
1050 		if (dataset != NULL) {
1051 			(void) snprintf(command, ZFS_MAXPROPLEN * 2,
1052 			    "echo %s set sharesmb=\"%s\" %s", COMMAND,
1053 			    opts != NULL ? opts : "off", dataset);
1054 			pfile = popen(command, "r");
1055 			if (pfile != NULL) {
1056 				ret = pclose(pfile);
1057 				if (ret != 0)
1058 					ret = SA_SYSTEM_ERR;
1059 			}
1060 		}
1061 		if (opts != NULL)
1062 			free(opts);
1063 		if (dataset != NULL)
1064 			free(dataset);
1065 		free(command);
1066 	}
1067 	return (ret);
1068 }
1069 
1070 /*
1071  * sa_zfs_update(group)
1072  *
1073  * call back to ZFS to update the share if necessary.
1074  * Don't do it if it isn't a real change.
1075  */
1076 int
1077 sa_zfs_update(sa_group_t group)
1078 {
1079 	sa_optionset_t protopt;
1080 	sa_group_t parent;
1081 	char *command;
1082 	char *optstring;
1083 	int ret = SA_OK;
1084 	int doupdate = 0;
1085 	FILE *pfile;
1086 
1087 	if (sa_is_share(group))
1088 		parent = sa_get_parent_group(group);
1089 	else
1090 		parent = group;
1091 
1092 	if (parent != NULL) {
1093 		command = malloc(ZFS_MAXPROPLEN * 2);
1094 		if (command == NULL)
1095 			return (SA_NO_MEMORY);
1096 
1097 		*command = '\0';
1098 		for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
1099 		    protopt = sa_get_next_optionset(protopt)) {
1100 
1101 			char *proto = sa_get_optionset_attr(protopt, "type");
1102 			char *path;
1103 			char *dataset = NULL;
1104 			char *zfsopts = NULL;
1105 
1106 			if (sa_is_share(group)) {
1107 				path = sa_get_share_attr((sa_share_t)group,
1108 				    "path");
1109 				if (path != NULL) {
1110 					sa_handle_impl_t impl_handle;
1111 
1112 					impl_handle = sa_find_group_handle(
1113 					    group);
1114 					if (impl_handle != NULL)
1115 						dataset = get_zfs_dataset(
1116 						    impl_handle, path, B_FALSE);
1117 					else
1118 						ret = SA_SYSTEM_ERR;
1119 
1120 					sa_free_attr_string(path);
1121 				}
1122 			} else {
1123 				dataset = sa_get_group_attr(group, "name");
1124 			}
1125 			/* update only when there is an optstring found */
1126 			doupdate = 0;
1127 			if (proto != NULL && dataset != NULL) {
1128 				optstring = sa_proto_legacy_format(proto,
1129 				    group, 1);
1130 				zfsopts = get_zfs_property(dataset,
1131 				    ZFS_PROP_SHARENFS);
1132 
1133 				if (optstring != NULL && zfsopts != NULL) {
1134 					if (strcmp(optstring, zfsopts) != 0)
1135 						doupdate++;
1136 				}
1137 				if (doupdate) {
1138 					if (optstring != NULL &&
1139 					    strlen(optstring) > 0) {
1140 						(void) snprintf(command,
1141 						    ZFS_MAXPROPLEN * 2,
1142 						    "%s set share%s=%s %s",
1143 						    COMMAND, proto,
1144 						    optstring, dataset);
1145 					} else {
1146 						(void) snprintf(command,
1147 						    ZFS_MAXPROPLEN * 2,
1148 						    "%s set share%s=on %s",
1149 						    COMMAND, proto,
1150 						    dataset);
1151 					}
1152 					pfile = popen(command, "r");
1153 					if (pfile != NULL)
1154 						ret = pclose(pfile);
1155 					switch (ret) {
1156 					default:
1157 					case 1:
1158 						ret = SA_SYSTEM_ERR;
1159 						break;
1160 					case 2:
1161 						ret = SA_SYNTAX_ERR;
1162 						break;
1163 					case 0:
1164 						break;
1165 					}
1166 				}
1167 				if (optstring != NULL)
1168 					free(optstring);
1169 				if (zfsopts != NULL)
1170 					free(zfsopts);
1171 			}
1172 			if (proto != NULL)
1173 				sa_free_attr_string(proto);
1174 			if (dataset != NULL)
1175 				free(dataset);
1176 		}
1177 		free(command);
1178 	}
1179 	return (ret);
1180 }
1181 
1182 /*
1183  * sa_group_is_zfs(group)
1184  *
1185  * Given the group, determine if the zfs attribute is set.
1186  */
1187 
1188 int
1189 sa_group_is_zfs(sa_group_t group)
1190 {
1191 	char *zfs;
1192 	int ret = 0;
1193 
1194 	zfs = sa_get_group_attr(group, "zfs");
1195 	if (zfs != NULL) {
1196 		ret = 1;
1197 		sa_free_attr_string(zfs);
1198 	}
1199 	return (ret);
1200 }
1201 
1202 /*
1203  * sa_path_is_zfs(path)
1204  *
1205  * Check to see if the file system path represents is of type "zfs".
1206  */
1207 
1208 int
1209 sa_path_is_zfs(char *path)
1210 {
1211 	char *fstype;
1212 	int ret = 0;
1213 
1214 	fstype = sa_fstype(path);
1215 	if (fstype != NULL && strcmp(fstype, "zfs") == 0)
1216 		ret = 1;
1217 	if (fstype != NULL)
1218 		sa_free_fstype(fstype);
1219 	return (ret);
1220 }
1221 
1222 int
1223 sa_sharetab_fill_zfs(sa_share_t share, share_t *sh, char *proto)
1224 {
1225 	char *path;
1226 
1227 	/* Make sure path is valid */
1228 
1229 	path = sa_get_share_attr(share, "path");
1230 	if (path != NULL) {
1231 		(void) memset(sh, 0, sizeof (sh));
1232 		(void) sa_fillshare(share, proto, sh);
1233 		sa_free_attr_string(path);
1234 		return (0);
1235 	} else
1236 		return (1);
1237 }
1238 
1239 #define	SMAX(i, j)	\
1240 	if ((j) > (i)) { \
1241 		(i) = (j); \
1242 	}
1243 
1244 int
1245 sa_share_zfs(sa_share_t share, char *path, share_t *sh,
1246     void *exportdata, zfs_share_op_t operation)
1247 {
1248 	libzfs_handle_t *libhandle;
1249 	sa_group_t group;
1250 	sa_handle_t sahandle;
1251 	char *dataset;
1252 	int err = EINVAL;
1253 	int i, j;
1254 	char newpath[MAXPATHLEN];
1255 	char *pathp;
1256 
1257 	/*
1258 	 * First find the dataset name
1259 	 */
1260 	if ((group = sa_get_parent_group(share)) == NULL)  {
1261 		return (SA_SYSTEM_ERR);
1262 	}
1263 	if ((sahandle = sa_find_group_handle(group)) == NULL) {
1264 		return (SA_SYSTEM_ERR);
1265 	}
1266 
1267 	/*
1268 	 * If get_zfs_dataset fails, see if it is a subdirectory
1269 	 */
1270 
1271 	pathp = path;
1272 	while ((dataset = get_zfs_dataset(sahandle, pathp, B_TRUE)) == NULL) {
1273 		char *p;
1274 
1275 		if (pathp == path) {
1276 			(void) strlcpy(newpath, path, sizeof (newpath));
1277 			pathp = newpath;
1278 		}
1279 
1280 		/*
1281 		 * chop off part of path, but if we are at root then
1282 		 * make sure path is a /
1283 		 */
1284 		if ((strlen(pathp) > 1) && (p = strrchr(pathp, '/'))) {
1285 			if (pathp == p) {
1286 				*(p + 1) = '\0';  /* skip over /, root case */
1287 			} else {
1288 				*p = '\0';
1289 			}
1290 		} else {
1291 			return (SA_SYSTEM_ERR);
1292 		}
1293 	}
1294 
1295 	libhandle = libzfs_init();
1296 	if (libhandle != NULL) {
1297 
1298 		i = (sh->sh_path ? strlen(sh->sh_path) : 0);
1299 		sh->sh_size = i;
1300 
1301 		j = (sh->sh_res ? strlen(sh->sh_res) : 0);
1302 		sh->sh_size += j;
1303 		SMAX(i, j);
1304 
1305 		j = (sh->sh_fstype ? strlen(sh->sh_fstype) : 0);
1306 		sh->sh_size += j;
1307 		SMAX(i, j);
1308 
1309 		j = (sh->sh_opts ? strlen(sh->sh_opts) : 0);
1310 		sh->sh_size += j;
1311 		SMAX(i, j);
1312 
1313 		j = (sh->sh_descr ? strlen(sh->sh_descr) : 0);
1314 		sh->sh_size += j;
1315 		SMAX(i, j);
1316 		err = zfs_deleg_share_nfs(libhandle, dataset, path,
1317 		    exportdata, sh, i, operation);
1318 		if (err == SA_OK)
1319 			sa_update_sharetab_ts(sahandle);
1320 		libzfs_fini(libhandle);
1321 	}
1322 	free(dataset);
1323 	return (err);
1324 }
1325 
1326 /*
1327  * sa_get_zfs_handle(handle)
1328  *
1329  * Given an sa_handle_t, return the libzfs_handle_t *. This is only
1330  * used internally by libzfs. Needed in order to avoid including
1331  * libshare_impl.h in libzfs.
1332  */
1333 
1334 libzfs_handle_t *
1335 sa_get_zfs_handle(sa_handle_t handle)
1336 {
1337 	sa_handle_impl_t implhandle = (sa_handle_impl_t)handle;
1338 
1339 	return (implhandle->zfs_libhandle);
1340 }
1341