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