xref: /illumos-gate/usr/src/lib/libshare/common/libshare_zfs.c (revision bea83d026ee1bd1b2a2419e1d0232f107a5d7d9b)
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 				set_node_attr(group, "zfs", "true");
481 			if (strcmp(optstring, "on") == 0)
482 				optstring = "rw";
483 			if (group != NULL) {
484 				options = strdup(optstring);
485 				if (options != NULL) {
486 					*err = sa_parse_legacy_options(group,
487 					    options, proto);
488 
489 					/* If no optionset, add one */
490 					if (sa_get_optionset(group, proto) ==
491 					    NULL)
492 						(void) sa_create_optionset(
493 						    group, proto);
494 					free(options);
495 				} else {
496 					*err = SA_NO_MEMORY;
497 				}
498 			}
499 		} else if (proto != NULL && strcmp(proto, "smb") == 0) {
500 			*err = SA_PROP_SHARE_ONLY;
501 		}
502 	}
503 	return (group);
504 }
505 
506 /*
507  * zfs_construct_resource(share, name, base, dataset)
508  *
509  * Add a resource to the share using name as a template. If name ==
510  * NULL, then construct a name based on the dataset value.
511  * name.
512  */
513 static void
514 zfs_construct_resource(sa_share_t share, char *dataset)
515 {
516 	char buff[SA_MAX_RESOURCE_NAME + 1];
517 	int ret = SA_OK;
518 
519 	(void) snprintf(buff, SA_MAX_RESOURCE_NAME, "%s", dataset);
520 	sa_fix_resource_name(buff);
521 	(void) sa_add_resource(share, buff, SA_SHARE_TRANSIENT, &ret);
522 }
523 
524 /*
525  * zfs_inherited(handle, source, sourcestr)
526  *
527  * handle case of inherited share{nfs,smb}. Pulled out of sa_get_zfs_shares
528  * for readability.
529  */
530 static int
531 zfs_inherited(sa_handle_t handle, sa_share_t share, char *sourcestr,
532     char *shareopts, char *mountpoint, char *proto, char *dataset)
533 {
534 	int doshopt = 0;
535 	int err = SA_OK;
536 	sa_group_t group;
537 	sa_resource_t resource;
538 	uint64_t features;
539 
540 	/*
541 	 * Need to find the "real" parent sub-group. It may not be
542 	 * mounted, but it was identified in the "sourcestr"
543 	 * variable. The real parent not mounted can occur if
544 	 * "canmount=off and sharenfs=on".
545 	 */
546 	group = find_or_create_zfs_subgroup(handle, sourcestr, proto,
547 	    shareopts, &doshopt);
548 	if (group != NULL) {
549 		/*
550 		 * We may need the first share for resource
551 		 * prototype. We only care about it if it has a
552 		 * resource that sets a prefix value.
553 		 */
554 		if (share == NULL)
555 			share = _sa_add_share(group, mountpoint,
556 			    SA_SHARE_TRANSIENT, &err,
557 			    (uint64_t)SA_FEATURE_NONE);
558 		/*
559 		 * some options may only be on shares. If the opt
560 		 * string contains one of those, we put it just on the
561 		 * share.
562 		 */
563 		if (share != NULL && doshopt == SA_PROP_SHARE_ONLY) {
564 			char *options;
565 			options = strdup(shareopts);
566 			if (options != NULL) {
567 				set_node_attr(share, "dataset", dataset);
568 				err = sa_parse_legacy_options(share, options,
569 				    proto);
570 				set_node_attr(share, "dataset", NULL);
571 				free(options);
572 			}
573 			if (sa_get_optionset(group, proto) == NULL)
574 				(void) sa_create_optionset(group, proto);
575 		}
576 		features = sa_proto_get_featureset(proto);
577 		if (share != NULL && features & SA_FEATURE_RESOURCE) {
578 			/*
579 			 * We have a share and the protocol requires
580 			 * that at least one resource exist (probably
581 			 * SMB). We need to make sure that there is at
582 			 * least one.
583 			 */
584 			resource = sa_get_share_resource(share, NULL);
585 			if (resource == NULL) {
586 				zfs_construct_resource(share, dataset);
587 			}
588 		}
589 	} else {
590 		err = SA_NO_MEMORY;
591 	}
592 	return (err);
593 }
594 
595 /*
596  * zfs_notinherited(group, share, mountpoint, shareopts, proto, dataset,
597  *     grouperr)
598  *
599  * handle case where this is the top of a sub-group in ZFS. Pulled out
600  * of sa_get_zfs_shares for readability. We need the grouperr from the
601  * creation of the subgroup to know whether to add the public
602  * property, etc. to the specific share.
603  */
604 static int
605 zfs_notinherited(sa_group_t group, sa_share_t share, char *mountpoint,
606     char *shareopts, char *proto, char *dataset, int grouperr)
607 {
608 	int err = SA_OK;
609 	sa_resource_t resource;
610 	uint64_t features;
611 
612 	set_node_attr(group, "zfs", "true");
613 	if (share == NULL)
614 		share = _sa_add_share(group, mountpoint, SA_SHARE_TRANSIENT,
615 		    &err, (uint64_t)SA_FEATURE_NONE);
616 	if (err == SA_OK) {
617 		if (strcmp(shareopts, "on") == 0)
618 			shareopts = "";
619 		if (shareopts != NULL) {
620 			char *options;
621 			if (grouperr == SA_PROP_SHARE_ONLY) {
622 				/*
623 				 * Some properties may only be on
624 				 * shares, but due to the ZFS
625 				 * sub-groups being artificial, we
626 				 * sometimes get this and have to deal
627 				 * with it. We do it by attempting to
628 				 * put it on the share.
629 				 */
630 				options = strdup(shareopts);
631 				if (options != NULL) {
632 					err = sa_parse_legacy_options(share,
633 					    options, proto);
634 					free(options);
635 				}
636 			}
637 			/* unmark the share's changed state */
638 			set_node_attr(share, "changed", NULL);
639 		}
640 		features = sa_proto_get_featureset(proto);
641 		if (share != NULL && features & SA_FEATURE_RESOURCE) {
642 			/*
643 			 * We have a share and the protocol requires
644 			 * that at least one resource exist (probably
645 			 * SMB). We need to make sure that there is at
646 			 * least one.
647 			 */
648 			resource = sa_get_share_resource(share, NULL);
649 			if (resource == NULL) {
650 				zfs_construct_resource(share, dataset);
651 			}
652 		}
653 	}
654 	return (err);
655 }
656 
657 /*
658  * zfs_grp_error(err)
659  *
660  * Print group create error, but only once. If err is 0 do the
661  * print else don't.
662  */
663 
664 static void
665 zfs_grp_error(int err)
666 {
667 	if (err == 0) {
668 		/* only print error once */
669 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
670 		    "Cannot create ZFS subgroup during initialization:"
671 		    " %s\n"), sa_errorstr(SA_SYSTEM_ERR));
672 	}
673 }
674 
675 /*
676  * zfs_process_share(handle, share, mountpoint, proto, source,
677  *     shareopts, sourcestr)
678  *
679  * Creates the subgroup, if necessary and adds shares, resources
680  * and properties.
681  */
682 int
683 sa_zfs_process_share(sa_handle_t handle, sa_group_t group, sa_share_t share,
684     char *mountpoint, char *proto, zprop_source_t source, char *shareopts,
685     char *sourcestr, char *dataset)
686 {
687 	int err = SA_OK;
688 
689 	if (source & ZPROP_SRC_INHERITED) {
690 		err = zfs_inherited(handle, share, sourcestr, shareopts,
691 		    mountpoint, proto, dataset);
692 	} else {
693 		group = find_or_create_zfs_subgroup(handle, dataset, proto,
694 		    shareopts, &err);
695 		if (group == NULL) {
696 			static boolean_t reported_error = B_FALSE;
697 			/*
698 			 * There is a problem, but we can't do
699 			 * anything about it at this point so we issue
700 			 * a warning and move on.
701 			 */
702 			zfs_grp_error(reported_error);
703 			reported_error = B_TRUE;
704 		}
705 		set_node_attr(group, "zfs", "true");
706 		/*
707 		 * Add share with local opts via zfs_notinherited.
708 		 */
709 		err = zfs_notinherited(group, share, mountpoint, shareopts,
710 		    proto, dataset, err);
711 	}
712 	return (err);
713 }
714 
715 /*
716  * sa_get_zfs_shares(handle, groupname)
717  *
718  * Walk the mnttab for all zfs mounts and determine which are
719  * shared. Find or create the appropriate group/sub-group to contain
720  * the shares.
721  *
722  * All shares are in a sub-group that will hold the properties. This
723  * allows representing the inherited property model.
724  */
725 
726 int
727 sa_get_zfs_shares(sa_handle_t handle, char *groupname)
728 {
729 	sa_group_t group;
730 	sa_group_t zfsgroup;
731 	int legacy = 0;
732 	int err;
733 	zfs_handle_t **zlist;
734 	char shareopts[ZFS_MAXPROPLEN];
735 	sa_share_t share;
736 	zprop_source_t source;
737 	char sourcestr[ZFS_MAXPROPLEN];
738 	char mountpoint[ZFS_MAXPROPLEN];
739 	size_t count = 0, i;
740 	libzfs_handle_t *zfs_libhandle;
741 
742 	/*
743 	 * If we can't access libzfs, don't bother doing anything.
744 	 */
745 	zfs_libhandle = ((sa_handle_impl_t)handle)->zfs_libhandle;
746 	if (zfs_libhandle == NULL)
747 		return (SA_SYSTEM_ERR);
748 
749 	zfsgroup = find_or_create_group(handle, groupname, NULL, &err);
750 	if (zfsgroup == NULL)
751 		return (legacy);
752 
753 	/*
754 	 * need to walk the mounted ZFS pools and datasets to
755 	 * find shares that are possible.
756 	 */
757 	get_all_filesystems((sa_handle_impl_t)handle, &zlist, &count);
758 	qsort(zlist, count, sizeof (void *), mountpoint_compare);
759 
760 	group = zfsgroup;
761 	for (i = 0; i < count; i++) {
762 		char *dataset;
763 		int foundnfs = 0;
764 
765 		source = ZPROP_SRC_ALL;
766 		/* If no mountpoint, skip. */
767 		if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT,
768 		    mountpoint, sizeof (mountpoint), NULL, NULL, 0,
769 		    B_FALSE) != 0)
770 			continue;
771 
772 		/*
773 		 * zfs_get_name value must not be freed. It is just a
774 		 * pointer to a value in the handle.
775 		 */
776 		if ((dataset = (char *)zfs_get_name(zlist[i])) == NULL)
777 			continue;
778 
779 		/*
780 		 * only deal with "mounted" file systems since
781 		 * unmounted file systems can't actually be shared.
782 		 */
783 
784 		if (!zfs_is_mounted(zlist[i], NULL))
785 			continue;
786 
787 		if (zfs_prop_get(zlist[i], ZFS_PROP_SHARENFS, shareopts,
788 		    sizeof (shareopts), &source, sourcestr,
789 		    ZFS_MAXPROPLEN, B_FALSE) == 0 &&
790 		    strcmp(shareopts, "off") != 0) {
791 			/* it is shared so add to list */
792 			err = SA_OK;
793 			foundnfs = 1;
794 			share = sa_find_share(handle, mountpoint);
795 			if (share != NULL) {
796 				/*
797 				 * A zfs file system had been shared
798 				 * through traditional methods
799 				 * (share/dfstab or added to a non-zfs
800 				 * group.  Now it has been added to a
801 				 * ZFS group via the zfs
802 				 * command. Remove from previous
803 				 * config and setup with current
804 				 * options.
805 				 */
806 				err = sa_remove_share(share);
807 				share = NULL;
808 			}
809 			if (err == SA_OK) {
810 				err = sa_zfs_process_share(handle, group,
811 				    share, mountpoint, "nfs", source,
812 				    shareopts, sourcestr, dataset);
813 			}
814 		}
815 		if (zfs_prop_get(zlist[i], ZFS_PROP_SHARESMB, shareopts,
816 		    sizeof (shareopts), &source, sourcestr,
817 		    ZFS_MAXPROPLEN, B_FALSE) == 0 &&
818 		    strcmp(shareopts, "off") != 0) {
819 			/* it is shared so add to list */
820 			err = SA_OK;
821 			share = sa_find_share(handle, mountpoint);
822 			if (share != NULL && !foundnfs) {
823 				/*
824 				 * A zfs file system had been shared
825 				 * through traditional methods
826 				 * (share/dfstab or added to a non-zfs
827 				 * group.  Now it has been added to a
828 				 * ZFS group via the zfs
829 				 * command. Remove from previous
830 				 * config and setup with current
831 				 * options.
832 				 */
833 				err = sa_remove_share(share);
834 				share = NULL;
835 			}
836 			if (err == SA_OK) {
837 				err = sa_zfs_process_share(handle, group,
838 				    share, mountpoint, "smb", source,
839 				    shareopts, sourcestr, dataset);
840 			}
841 		}
842 	}
843 	/*
844 	 * Don't need to free the "zlist" variable since it is only a
845 	 * pointer to a cached value that will be freed when
846 	 * sa_fini() is called.
847 	 */
848 	return (legacy);
849 }
850 
851 #define	COMMAND		"/usr/sbin/zfs"
852 
853 /*
854  * sa_zfs_set_sharenfs(group, path, on)
855  *
856  * Update the "sharenfs" property on the path. If on is true, then set
857  * to the properties on the group or "on" if no properties are
858  * defined. Set to "off" if on is false.
859  */
860 
861 int
862 sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
863 {
864 	int ret = SA_NOT_IMPLEMENTED;
865 	char *command;
866 
867 	command = malloc(ZFS_MAXPROPLEN * 2);
868 	if (command != NULL) {
869 		char *opts = NULL;
870 		char *dataset = NULL;
871 		FILE *pfile;
872 		sa_handle_impl_t impl_handle;
873 		/* for now, NFS is always available for "zfs" */
874 		if (on) {
875 			opts = sa_proto_legacy_format("nfs", group, 1);
876 			if (opts != NULL && strlen(opts) == 0) {
877 				free(opts);
878 				opts = strdup("on");
879 			}
880 		}
881 
882 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
883 		assert(impl_handle != NULL);
884 		if (impl_handle != NULL)
885 			dataset = get_zfs_dataset(impl_handle, path, B_FALSE);
886 		else
887 			ret = SA_SYSTEM_ERR;
888 
889 		if (dataset != NULL) {
890 			(void) snprintf(command, ZFS_MAXPROPLEN * 2,
891 			    "%s set sharenfs=\"%s\" %s", COMMAND,
892 			    opts != NULL ? opts : "off", dataset);
893 			pfile = popen(command, "r");
894 			if (pfile != NULL) {
895 				ret = pclose(pfile);
896 				if (ret != 0)
897 					ret = SA_SYSTEM_ERR;
898 			}
899 		}
900 		if (opts != NULL)
901 			free(opts);
902 		if (dataset != NULL)
903 			free(dataset);
904 		free(command);
905 	}
906 	return (ret);
907 }
908 
909 /*
910  * add_resources(share, opt)
911  *
912  * Add resource properties to those in "opt".  Resources are prefixed
913  * with name=resourcename.
914  */
915 static char *
916 add_resources(sa_share_t share, char *opt)
917 {
918 	char *newopt = NULL;
919 	char *propstr;
920 	sa_resource_t resource;
921 
922 	newopt = strdup(opt);
923 	if (newopt == NULL)
924 		return (newopt);
925 
926 	for (resource = sa_get_share_resource(share, NULL);
927 	    resource != NULL;
928 	    resource = sa_get_next_resource(resource)) {
929 		char *name;
930 		size_t size;
931 
932 		name = sa_get_resource_attr(resource, "name");
933 		if (name == NULL) {
934 			free(newopt);
935 			return (NULL);
936 		}
937 		size = strlen(name) + strlen(opt) + sizeof ("name=") + 1;
938 		newopt = calloc(1, size);
939 		if (newopt != NULL)
940 			(void) snprintf(newopt, size, "%s,name=%s", opt, name);
941 		free(opt);
942 		opt = newopt;
943 		propstr = sa_proto_legacy_format("smb", resource, 0);
944 		if (propstr == NULL) {
945 			free(opt);
946 			return (NULL);
947 		}
948 		size = strlen(propstr) + strlen(opt) + 2;
949 		newopt = calloc(1, size);
950 		if (newopt != NULL)
951 			(void) snprintf(newopt, size, "%s,%s", opt, propstr);
952 		free(opt);
953 		opt = newopt;
954 	}
955 	return (opt);
956 }
957 
958 /*
959  * sa_zfs_set_sharesmb(group, path, on)
960  *
961  * Update the "sharesmb" property on the path. If on is true, then set
962  * to the properties on the group or "on" if no properties are
963  * defined. Set to "off" if on is false.
964  */
965 
966 int
967 sa_zfs_set_sharesmb(sa_group_t group, char *path, int on)
968 {
969 	int ret = SA_NOT_IMPLEMENTED;
970 	char *command;
971 	sa_share_t share;
972 
973 	/* In case SMB not enabled */
974 	if (sa_get_optionset(group, "smb") == NULL)
975 		return (SA_NOT_SUPPORTED);
976 
977 	command = malloc(ZFS_MAXPROPLEN * 2);
978 	if (command != NULL) {
979 		char *opts = NULL;
980 		char *dataset = NULL;
981 		FILE *pfile;
982 		sa_handle_impl_t impl_handle;
983 
984 		if (on) {
985 			char *newopt;
986 
987 			share = sa_get_share(group, NULL);
988 			opts = sa_proto_legacy_format("smb", share, 1);
989 			if (opts != NULL && strlen(opts) == 0) {
990 				free(opts);
991 				opts = strdup("on");
992 			}
993 			newopt = add_resources(opts, share);
994 			free(opts);
995 			opts = newopt;
996 		}
997 
998 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
999 		assert(impl_handle != NULL);
1000 		if (impl_handle != NULL)
1001 			dataset = get_zfs_dataset(impl_handle, path, B_FALSE);
1002 		else
1003 			ret = SA_SYSTEM_ERR;
1004 
1005 		if (dataset != NULL) {
1006 			(void) snprintf(command, ZFS_MAXPROPLEN * 2,
1007 			    "echo %s set sharesmb=\"%s\" %s", COMMAND,
1008 			    opts != NULL ? opts : "off", dataset);
1009 			pfile = popen(command, "r");
1010 			if (pfile != NULL) {
1011 				ret = pclose(pfile);
1012 				if (ret != 0)
1013 					ret = SA_SYSTEM_ERR;
1014 			}
1015 		}
1016 		if (opts != NULL)
1017 			free(opts);
1018 		if (dataset != NULL)
1019 			free(dataset);
1020 		free(command);
1021 	}
1022 	return (ret);
1023 }
1024 
1025 /*
1026  * sa_zfs_update(group)
1027  *
1028  * call back to ZFS to update the share if necessary.
1029  * Don't do it if it isn't a real change.
1030  */
1031 int
1032 sa_zfs_update(sa_group_t group)
1033 {
1034 	sa_optionset_t protopt;
1035 	sa_group_t parent;
1036 	char *command;
1037 	char *optstring;
1038 	int ret = SA_OK;
1039 	int doupdate = 0;
1040 	FILE *pfile;
1041 
1042 	if (sa_is_share(group))
1043 		parent = sa_get_parent_group(group);
1044 	else
1045 		parent = group;
1046 
1047 	if (parent != NULL) {
1048 		command = malloc(ZFS_MAXPROPLEN * 2);
1049 		if (command == NULL)
1050 			return (SA_NO_MEMORY);
1051 
1052 		*command = '\0';
1053 		for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
1054 		    protopt = sa_get_next_optionset(protopt)) {
1055 
1056 			char *proto = sa_get_optionset_attr(protopt, "type");
1057 			char *path;
1058 			char *dataset = NULL;
1059 			char *zfsopts = NULL;
1060 
1061 			if (sa_is_share(group)) {
1062 				path = sa_get_share_attr((sa_share_t)group,
1063 				    "path");
1064 				if (path != NULL) {
1065 					sa_handle_impl_t impl_handle;
1066 
1067 					impl_handle = sa_find_group_handle(
1068 					    group);
1069 					if (impl_handle != NULL)
1070 						dataset = get_zfs_dataset(
1071 						    impl_handle, path, B_FALSE);
1072 					else
1073 						ret = SA_SYSTEM_ERR;
1074 
1075 					sa_free_attr_string(path);
1076 				}
1077 			} else {
1078 				dataset = sa_get_group_attr(group, "name");
1079 			}
1080 			/* update only when there is an optstring found */
1081 			doupdate = 0;
1082 			if (proto != NULL && dataset != NULL) {
1083 				optstring = sa_proto_legacy_format(proto,
1084 				    group, 1);
1085 				zfsopts = get_zfs_property(dataset,
1086 				    ZFS_PROP_SHARENFS);
1087 
1088 				if (optstring != NULL && zfsopts != NULL) {
1089 					if (strcmp(optstring, zfsopts) != 0)
1090 						doupdate++;
1091 				}
1092 				if (doupdate) {
1093 					if (optstring != NULL &&
1094 					    strlen(optstring) > 0) {
1095 						(void) snprintf(command,
1096 						    ZFS_MAXPROPLEN * 2,
1097 						    "%s set sharenfs=%s %s",
1098 						    COMMAND,
1099 						    optstring, dataset);
1100 					} else {
1101 						(void) snprintf(command,
1102 						    ZFS_MAXPROPLEN * 2,
1103 						    "%s set sharenfs=on %s",
1104 						    COMMAND,
1105 						    dataset);
1106 					}
1107 					pfile = popen(command, "r");
1108 					if (pfile != NULL)
1109 						ret = pclose(pfile);
1110 					switch (ret) {
1111 					default:
1112 					case 1:
1113 						ret = SA_SYSTEM_ERR;
1114 						break;
1115 					case 2:
1116 						ret = SA_SYNTAX_ERR;
1117 						break;
1118 					case 0:
1119 						break;
1120 					}
1121 				}
1122 				if (optstring != NULL)
1123 					free(optstring);
1124 				if (zfsopts != NULL)
1125 					free(zfsopts);
1126 			}
1127 			if (proto != NULL)
1128 				sa_free_attr_string(proto);
1129 			if (dataset != NULL)
1130 				free(dataset);
1131 		}
1132 		free(command);
1133 	}
1134 	return (ret);
1135 }
1136 
1137 /*
1138  * sa_group_is_zfs(group)
1139  *
1140  * Given the group, determine if the zfs attribute is set.
1141  */
1142 
1143 int
1144 sa_group_is_zfs(sa_group_t group)
1145 {
1146 	char *zfs;
1147 	int ret = 0;
1148 
1149 	zfs = sa_get_group_attr(group, "zfs");
1150 	if (zfs != NULL) {
1151 		ret = 1;
1152 		sa_free_attr_string(zfs);
1153 	}
1154 	return (ret);
1155 }
1156 
1157 /*
1158  * sa_path_is_zfs(path)
1159  *
1160  * Check to see if the file system path represents is of type "zfs".
1161  */
1162 
1163 int
1164 sa_path_is_zfs(char *path)
1165 {
1166 	char *fstype;
1167 	int ret = 0;
1168 
1169 	fstype = sa_fstype(path);
1170 	if (fstype != NULL && strcmp(fstype, "zfs") == 0)
1171 		ret = 1;
1172 	if (fstype != NULL)
1173 		sa_free_fstype(fstype);
1174 	return (ret);
1175 }
1176 
1177 int
1178 sa_sharetab_fill_zfs(sa_share_t share, share_t *sh, char *proto)
1179 {
1180 	char *path;
1181 
1182 	/* Make sure path is valid */
1183 
1184 	path = sa_get_share_attr(share, "path");
1185 	if (path != NULL) {
1186 		(void) memset(sh, 0, sizeof (sh));
1187 		(void) sa_fillshare(share, proto, sh);
1188 		sa_free_attr_string(path);
1189 		return (0);
1190 	} else
1191 		return (1);
1192 }
1193 
1194 #define	SMAX(i, j)	\
1195 	if ((j) > (i)) { \
1196 		(i) = (j); \
1197 	}
1198 
1199 int
1200 sa_share_zfs(sa_share_t share, char *path, share_t *sh,
1201     void *exportdata, zfs_share_op_t operation)
1202 {
1203 	libzfs_handle_t *libhandle;
1204 	sa_group_t group;
1205 	sa_handle_t sahandle;
1206 	char *dataset;
1207 	int err = EINVAL;
1208 	int i, j;
1209 	char newpath[MAXPATHLEN];
1210 	char *pathp;
1211 
1212 	/*
1213 	 * First find the dataset name
1214 	 */
1215 	if ((group = sa_get_parent_group(share)) == NULL)  {
1216 		return (SA_SYSTEM_ERR);
1217 	}
1218 	if ((sahandle = sa_find_group_handle(group)) == NULL) {
1219 		return (SA_SYSTEM_ERR);
1220 	}
1221 
1222 	/*
1223 	 * If get_zfs_dataset fails, see if it is a subdirectory
1224 	 */
1225 
1226 	pathp = path;
1227 	while ((dataset = get_zfs_dataset(sahandle, pathp, B_TRUE)) == NULL) {
1228 		char *p;
1229 
1230 		if (pathp == path) {
1231 			(void) strlcpy(newpath, path, sizeof (newpath));
1232 			pathp = newpath;
1233 		}
1234 
1235 		/*
1236 		 * chop off part of path, but if we are at root then
1237 		 * make sure path is a /
1238 		 */
1239 		if ((strlen(pathp) > 1) && (p = strrchr(pathp, '/'))) {
1240 			if (pathp == p) {
1241 				*(p + 1) = '\0';  /* skip over /, root case */
1242 			} else {
1243 				*p = '\0';
1244 			}
1245 		} else {
1246 			return (SA_SYSTEM_ERR);
1247 		}
1248 	}
1249 
1250 	libhandle = libzfs_init();
1251 	if (libhandle != NULL) {
1252 
1253 		i = (sh->sh_path ? strlen(sh->sh_path) : 0);
1254 		sh->sh_size = i;
1255 
1256 		j = (sh->sh_res ? strlen(sh->sh_res) : 0);
1257 		sh->sh_size += j;
1258 		SMAX(i, j);
1259 
1260 		j = (sh->sh_fstype ? strlen(sh->sh_fstype) : 0);
1261 		sh->sh_size += j;
1262 		SMAX(i, j);
1263 
1264 		j = (sh->sh_opts ? strlen(sh->sh_opts) : 0);
1265 		sh->sh_size += j;
1266 		SMAX(i, j);
1267 
1268 		j = (sh->sh_descr ? strlen(sh->sh_descr) : 0);
1269 		sh->sh_size += j;
1270 		SMAX(i, j);
1271 		err = zfs_deleg_share_nfs(libhandle, dataset, path,
1272 		    exportdata, sh, i, operation);
1273 		if (err == SA_OK)
1274 			sa_update_sharetab_ts(sahandle);
1275 		libzfs_fini(libhandle);
1276 	}
1277 	free(dataset);
1278 	return (err);
1279 }
1280 
1281 /*
1282  * sa_get_zfs_handle(handle)
1283  *
1284  * Given an sa_handle_t, return the libzfs_handle_t *. This is only
1285  * used internally by libzfs. Needed in order to avoid including
1286  * libshare_impl.h in libzfs.
1287  */
1288 
1289 libzfs_handle_t *
1290 sa_get_zfs_handle(sa_handle_t handle)
1291 {
1292 	sa_handle_impl_t implhandle = (sa_handle_impl_t)handle;
1293 
1294 	return (implhandle->zfs_libhandle);
1295 }
1296