xref: /illumos-gate/usr/src/lib/libshare/common/libshare_zfs.c (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
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 2007 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 <libzfs.h>
30 #include <string.h>
31 #include <libshare.h>
32 #include "libshare_impl.h"
33 #include <libintl.h>
34 
35 extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *);
36 extern sa_group_t _sa_create_zfs_group(sa_group_t, char *);
37 extern char *sa_fstype(char *);
38 extern void set_node_attr(void *, char *, char *);
39 extern int sa_is_share(void *);
40 
41 /*
42  * File system specific code for ZFS. The original code was stolen
43  * from the "zfs" command and modified to better suit this library's
44  * usage.
45  */
46 
47 typedef struct get_all_cbdata {
48 	zfs_handle_t	**cb_handles;
49 	size_t		cb_alloc;
50 	size_t		cb_used;
51 } get_all_cbdata_t;
52 
53 /*
54  * sa_zfs_init(impl_handle)
55  *
56  * Initialize an access handle into libzfs.  The handle needs to stay
57  * around until sa_zfs_fini() in order to maintain the cache of
58  * mounts.
59  */
60 
61 void
62 sa_zfs_init(sa_handle_impl_t impl_handle)
63 {
64 	impl_handle->zfs_libhandle = libzfs_init();
65 	libzfs_print_on_error(impl_handle->zfs_libhandle, B_TRUE);
66 }
67 
68 /*
69  * sa_zfs_fini(impl_handle)
70  *
71  * cleanup data structures and the libzfs handle used for accessing
72  * zfs file share info.
73  */
74 
75 void
76 sa_zfs_fini(sa_handle_impl_t impl_handle)
77 {
78 	if (impl_handle->zfs_libhandle != NULL) {
79 	    libzfs_fini(impl_handle->zfs_libhandle);
80 	    impl_handle->zfs_libhandle = NULL;
81 	    if (impl_handle->zfs_list != NULL) {
82 		/*
83 		 * contents of zfs_list were already freed by the call to
84 		 * libzfs_fini().
85 		 */
86 		free(impl_handle->zfs_list);
87 		impl_handle->zfs_list = NULL;
88 		impl_handle->zfs_list_count = 0;
89 	    }
90 	}
91 }
92 
93 /*
94  * get_one_filesystem(zfs_handle_t, data)
95  *
96  * an interator function called while iterating through the ZFS
97  * root. It accumulates into an array of file system handles that can
98  * be used to derive info about those file systems.
99  */
100 
101 static int
102 get_one_filesystem(zfs_handle_t *zhp, void *data)
103 {
104 	get_all_cbdata_t *cbp = data;
105 
106 	/*
107 	 * Skip any zvols
108 	 */
109 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
110 		zfs_close(zhp);
111 		return (0);
112 	}
113 
114 	if (cbp->cb_alloc == cbp->cb_used) {
115 		zfs_handle_t **handles;
116 
117 		if (cbp->cb_alloc == 0)
118 			cbp->cb_alloc = 64;
119 		else
120 			cbp->cb_alloc *= 2;
121 
122 		handles = calloc(1, cbp->cb_alloc * sizeof (void *));
123 		if (handles == NULL) {
124 		    return (0);
125 		}
126 
127 		if (cbp->cb_handles) {
128 			(void) memcpy(handles, cbp->cb_handles,
129 			    cbp->cb_used * sizeof (void *));
130 			free(cbp->cb_handles);
131 		}
132 
133 		cbp->cb_handles = handles;
134 	}
135 
136 	cbp->cb_handles[cbp->cb_used++] = zhp;
137 
138 	return (zfs_iter_filesystems(zhp, get_one_filesystem, data));
139 }
140 
141 /*
142  * get_all_filesystems(zfs_handle_t ***fslist, size_t *count)
143  *
144  * iterate through all ZFS file systems starting at the root. Returns
145  * a count and an array of handle pointers. Allocating is only done
146  * once. The caller does not need to free since it will be done at
147  * sa_zfs_fini() time.
148  */
149 
150 static void
151 get_all_filesystems(sa_handle_impl_t impl_handle,
152 			zfs_handle_t ***fslist, size_t *count)
153 {
154 	get_all_cbdata_t cb = { 0 };
155 
156 	if (impl_handle->zfs_list != NULL) {
157 	    *fslist = impl_handle->zfs_list;
158 	    *count = impl_handle->zfs_list_count;
159 	    return;
160 	}
161 
162 	(void) zfs_iter_root(impl_handle->zfs_libhandle,
163 				get_one_filesystem, &cb);
164 
165 	impl_handle->zfs_list = *fslist = cb.cb_handles;
166 	impl_handle->zfs_list_count = *count = cb.cb_used;
167 }
168 
169 /*
170  * mountpoint_compare(a, b)
171  *
172  * compares the mountpoint on two zfs file systems handles.
173  * returns values following strcmp() model.
174  */
175 
176 static int
177 mountpoint_compare(const void *a, const void *b)
178 {
179 	zfs_handle_t **za = (zfs_handle_t **)a;
180 	zfs_handle_t **zb = (zfs_handle_t **)b;
181 	char mounta[MAXPATHLEN];
182 	char mountb[MAXPATHLEN];
183 
184 	verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
185 	    sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
186 	verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
187 	    sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
188 
189 	return (strcmp(mounta, mountb));
190 }
191 
192 /*
193  * get_zfs_dataset(impl_handle, path)
194  *
195  * get the name of the ZFS dataset the path is equivalent to.  The
196  * dataset name is used for get/set of ZFS properties since libzfs
197  * requires a dataset to do a zfs_open().
198  */
199 
200 static char *
201 get_zfs_dataset(sa_handle_impl_t impl_handle, char *path)
202 {
203 	size_t i, count = 0;
204 	char *dataset = NULL;
205 	zfs_handle_t **zlist;
206 	char mountpoint[ZFS_MAXPROPLEN];
207 	char canmount[ZFS_MAXPROPLEN];
208 
209 	get_all_filesystems(impl_handle, &zlist, &count);
210 	qsort(zlist, count, sizeof (void *), mountpoint_compare);
211 	for (i = 0; i < count; i++) {
212 	    /* must have a mountpoint */
213 	    if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint,
214 		sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
215 		/* no mountpoint */
216 		continue;
217 	    }
218 
219 	    /* mountpoint must be a path */
220 	    if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
221 		strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0)
222 		continue;
223 
224 	    /* canmount must be set */
225 	    canmount[0] = '\0';
226 	    if (!zfs_prop_get(zlist[i], ZFS_PROP_CANMOUNT, canmount,
227 		    sizeof (canmount), NULL, NULL, 0, B_FALSE) != 0 ||
228 		strcmp(canmount, "off") == 0)
229 		continue;
230 
231 	/*
232 	 * have a mountable handle but want to skip those marked none
233 	 * and legacy
234 	 */
235 	    if (strcmp(mountpoint, path) == 0) {
236 		dataset = (char *)zfs_get_name(zlist[i]);
237 		break;
238 	    }
239 
240 	}
241 
242 	if (dataset != NULL) {
243 	    dataset = strdup(dataset);
244 	}
245 	return (dataset);
246 }
247 
248 /*
249  * get_zfs_property(dataset, property)
250  *
251  * Get the file system property specified from the ZFS dataset.
252  */
253 
254 static char *
255 get_zfs_property(char *dataset, zfs_prop_t property)
256 {
257 	zfs_handle_t *handle = NULL;
258 	char shareopts[ZFS_MAXPROPLEN];
259 	libzfs_handle_t *libhandle;
260 
261 	libhandle = libzfs_init();
262 	if (libhandle != NULL) {
263 	    handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
264 	    if (handle != NULL) {
265 		if (zfs_prop_get(handle, property, shareopts,
266 				sizeof (shareopts), NULL, NULL, 0,
267 				B_FALSE) == 0) {
268 		    zfs_close(handle);
269 		    libzfs_fini(libhandle);
270 		    return (strdup(shareopts));
271 		}
272 		zfs_close(handle);
273 	    }
274 	    libzfs_fini(libhandle);
275 	}
276 	return (NULL);
277 }
278 
279 /*
280  * sa_zfs_is_shared(handle, path)
281  *
282  * Check to see if the ZFS path provided has the sharenfs option set
283  * or not.
284  */
285 
286 int
287 sa_zfs_is_shared(sa_handle_t sahandle, char *path)
288 {
289 	int ret = 0;
290 	char *dataset;
291 	zfs_handle_t *handle = NULL;
292 	char shareopts[ZFS_MAXPROPLEN];
293 	libzfs_handle_t *libhandle;
294 
295 	dataset = get_zfs_dataset((sa_handle_t)sahandle, path);
296 	if (dataset != NULL) {
297 	    libhandle = libzfs_init();
298 	    if (libhandle != NULL) {
299 		handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
300 		if (handle != NULL) {
301 		    if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts,
302 					sizeof (shareopts), NULL, NULL, 0,
303 					B_FALSE) == 0 &&
304 			strcmp(shareopts, "off") != 0)
305 			ret = 1; /* it is shared */
306 		    zfs_close(handle);
307 		}
308 		libzfs_fini(libhandle);
309 	    }
310 	    free(dataset);
311 	}
312 	return (ret);
313 }
314 
315 /*
316  * find_or_create_group(groupname, proto, *err)
317  *
318  * While walking the ZFS tree, we need to add shares to a defined
319  * group. If the group doesn't exist, create it first, making sure it
320  * is marked as a ZFS group.
321  *
322  * Note that all ZFS shares are in a subgroup of the top level group
323  * called "zfs".
324  */
325 
326 static sa_group_t
327 find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err)
328 {
329 	sa_group_t group;
330 	sa_optionset_t optionset;
331 	int ret = SA_OK;
332 
333 	/*
334 	 * we check to see if the "zfs" group exists. Since this
335 	 * should be the top level group, we don't want the
336 	 * parent. This is to make sure the zfs group has been created
337 	 * and to created if it hasn't been.
338 	 */
339 	group = sa_get_group(handle, groupname);
340 	if (group == NULL) {
341 	    group = sa_create_group(handle, groupname, &ret);
342 
343 	    /* make sure this is flagged as a ZFS group */
344 	    if (group != NULL)
345 		ret = sa_set_group_attr(group, "zfs", "true");
346 	}
347 	if (group != NULL) {
348 	    if (proto != NULL) {
349 		optionset = sa_get_optionset(group, proto);
350 		if (optionset == NULL) {
351 		    optionset = sa_create_optionset(group, proto);
352 		} else {
353 		    char **protolist;
354 		    int numprotos, i;
355 		    numprotos = sa_get_protocols(&protolist);
356 		    for (i = 0; i < numprotos; i++) {
357 			optionset = sa_create_optionset(group, protolist[i]);
358 		    }
359 		    if (protolist != NULL)
360 			free(protolist);
361 		}
362 	    }
363 	}
364 	if (err != NULL)
365 	    *err = ret;
366 	return (group);
367 }
368 
369 /*
370  * find_or_create_zfs_subgroup(groupname, optstring, *err)
371  *
372  * ZFS shares will be in a subgroup of the "zfs" master group.  This
373  * function looks to see if the groupname exists and returns it if it
374  * does or else creates a new one with the specified name and returns
375  * that.  The "zfs" group will exist before we get here, but we make
376  * sure just in case.
377  *
378  * err must be a valid pointer.
379  */
380 
381 static sa_group_t
382 find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname,
383 				char *optstring, int *err)
384 {
385 	sa_group_t group = NULL;
386 	sa_group_t zfs;
387 	char *name;
388 	char *options;
389 
390 	/* start with the top-level "zfs" group */
391 	zfs = sa_get_group(handle, "zfs");
392 	*err = SA_OK;
393 	if (zfs != NULL) {
394 	    for (group = sa_get_sub_group(zfs); group != NULL;
395 		group = sa_get_next_group(group)) {
396 		name = sa_get_group_attr(group, "name");
397 		if (name != NULL && strcmp(name, groupname) == 0) {
398 		    /* have the group so break out of here */
399 		    sa_free_attr_string(name);
400 		    break;
401 		}
402 		if (name != NULL)
403 		    sa_free_attr_string(name);
404 	    }
405 
406 	    if (group == NULL) {
407 		/* need to create the sub-group since it doesn't exist */
408 		group = _sa_create_zfs_group(zfs, groupname);
409 		if (group != NULL) {
410 		    set_node_attr(group, "zfs", "true");
411 		}
412 		if (strcmp(optstring, "on") == 0)
413 		    optstring = "rw";
414 		if (group != NULL) {
415 		    options = strdup(optstring);
416 		    if (options != NULL) {
417 			*err = sa_parse_legacy_options(group, options, "nfs");
418 			free(options);
419 		    } else {
420 			*err = SA_NO_MEMORY;
421 		    }
422 		}
423 	    }
424 	}
425 	return (group);
426 }
427 
428 /*
429  * sa_get_zfs_shares(handle, groupname)
430  *
431  * Walk the mnttab for all zfs mounts and determine which are
432  * shared. Find or create the appropriate group/sub-group to contain
433  * the shares.
434  *
435  * All shares are in a sub-group that will hold the properties. This
436  * allows representing the inherited property model.
437  */
438 
439 int
440 sa_get_zfs_shares(sa_handle_t handle, char *groupname)
441 {
442 	sa_group_t group;
443 	sa_group_t zfsgroup;
444 	int legacy = 0;
445 	int err;
446 	zfs_handle_t **zlist;
447 	char shareopts[ZFS_MAXPROPLEN];
448 	sa_share_t share;
449 	zfs_source_t source;
450 	char sourcestr[ZFS_MAXPROPLEN];
451 	char mountpoint[ZFS_MAXPROPLEN];
452 	char *options;
453 	size_t count = 0, i;
454 	libzfs_handle_t *zfs_libhandle;
455 
456 	/*
457 	 * If we can't access libzfs, don't bother doing anything.
458 	 */
459 	zfs_libhandle = ((sa_handle_impl_t)handle)->zfs_libhandle;
460 	if (zfs_libhandle == NULL)
461 	    return (SA_SYSTEM_ERR);
462 
463 	zfsgroup = find_or_create_group(handle, groupname, "nfs", &err);
464 	if (zfsgroup != NULL) {
465 		/*
466 		 * need to walk the mounted ZFS pools and datasets to
467 		 * find shares that are possible.
468 		 */
469 	    get_all_filesystems((sa_handle_impl_t)handle, &zlist, &count);
470 	    qsort(zlist, count, sizeof (void *), mountpoint_compare);
471 
472 	    group = zfsgroup;
473 	    for (i = 0; i < count; i++) {
474 		char *dataset;
475 
476 		source = ZFS_SRC_ALL;
477 		if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint,
478 					sizeof (mountpoint), NULL, NULL, 0,
479 					B_FALSE) != 0) {
480 		    /* no mountpoint */
481 		    continue;
482 		}
483 
484 		/*
485 		 * zfs_get_name value must not be freed. It is just a
486 		 * pointer to a value in the handle.
487 		 */
488 		if ((dataset = (char *)zfs_get_name(zlist[i])) == NULL)
489 		    continue;
490 
491 		/*
492 		 * only deal with "mounted" file systems since
493 		 * unmounted file systems can't actually be shared.
494 		 */
495 
496 		if (!zfs_is_mounted(zlist[i], NULL))
497 		    continue;
498 
499 		if (zfs_prop_get(zlist[i], ZFS_PROP_SHARENFS, shareopts,
500 					sizeof (shareopts), &source, sourcestr,
501 					ZFS_MAXPROPLEN,
502 					B_FALSE) == 0 &&
503 			strcmp(shareopts, "off") != 0) {
504 		    /* it is shared so add to list */
505 		    share = sa_find_share(handle, mountpoint);
506 		    err = SA_OK;
507 		    if (share != NULL) {
508 			/*
509 			 * A zfs file system had been shared
510 			 * through traditional methods
511 			 * (share/dfstab or added to a non-zfs
512 			 * group.  Now it has been added to a
513 			 * ZFS group via the zfs
514 			 * command. Remove from previous
515 			 * config and setup with current
516 			 * options.
517 			 */
518 			err = sa_remove_share(share);
519 			share = NULL;
520 		    }
521 		    if (err == SA_OK) {
522 			if (source & ZFS_SRC_INHERITED) {
523 			    int doshopt = 0;
524 			/*
525 			 * Need to find the "real" parent
526 			 * sub-group. It may not be mounted, but it
527 			 * was identified in the "sourcestr"
528 			 * variable. The real parent not mounted can
529 			 * occur if "canmount=off and sharenfs=on".
530 			 */
531 			    group = find_or_create_zfs_subgroup(handle,
532 							sourcestr,
533 							shareopts, &doshopt);
534 			    if (group != NULL) {
535 				share = _sa_add_share(group, mountpoint,
536 							SA_SHARE_TRANSIENT,
537 							&err);
538 				/*
539 				 * some options may only be on
540 				 * shares. If the opt string
541 				 * contains one of those, we
542 				 * put it just on the share.
543 				 */
544 				if (share != NULL &&
545 				    doshopt == SA_PROP_SHARE_ONLY) {
546 				    options = strdup(shareopts);
547 				    if (options != NULL) {
548 					err = sa_parse_legacy_options(share,
549 								options, "nfs");
550 					free(options);
551 				    }
552 				}
553 			    } else {
554 				err = SA_NO_MEMORY;
555 			    }
556 			} else {
557 			    group = _sa_create_zfs_group(zfsgroup, dataset);
558 			    if (group == NULL) {
559 				static int err = 0;
560 				/*
561 				 * there is a problem, but we can't do
562 				 * anything about it at this point so
563 				 * we issue a warning an move on.
564 				 */
565 				if (err == 0) {
566 				    /* only print error once */
567 				    (void) fprintf(stderr,
568 					dgettext(TEXT_DOMAIN,
569 						"Cannot create ZFS subgroup "
570 						"during initialization:"
571 						" %s\n"),
572 					sa_errorstr(SA_SYSTEM_ERR));
573 				    err = 1;
574 				}
575 				continue;
576 			    }
577 			    set_node_attr(group, "zfs", "true");
578 			    share = _sa_add_share(group, mountpoint,
579 						SA_SHARE_TRANSIENT, &err);
580 			    if (err == SA_OK) {
581 				if (strcmp(shareopts, "on") != 0) {
582 				    options = strdup(shareopts);
583 				    if (options != NULL) {
584 					err = sa_parse_legacy_options(group,
585 									options,
586 									"nfs");
587 					free(options);
588 				    }
589 				    if (err == SA_PROP_SHARE_ONLY) {
590 					/*
591 					 * Same as above, some
592 					 * properties may only be on
593 					 * shares, but due to the ZFS
594 					 * sub-groups being
595 					 * artificial, we sometimes
596 					 * get this and have to deal
597 					 * with it. We do it by
598 					 * attempting to put it on the
599 					 * share.
600 					 */
601 					options = strdup(shareopts);
602 					if (options != NULL)
603 					    err = sa_parse_legacy_options(
604 									share,
605 									options,
606 									"nfs");
607 					    free(options);
608 					}
609 				    /* unmark the share's changed state */
610 				    set_node_attr(share, "changed", NULL);
611 				}
612 			    }
613 			}
614 		    }
615 		}
616 	    }
617 	}
618 	/*
619 	 * Don't need to free the "zlist" variable since it is only a
620 	 * pointer to a cached value that will be freed when
621 	 * sa_fini() is called.
622 	 */
623 	return (legacy);
624 }
625 
626 #define	COMMAND		"/usr/sbin/zfs"
627 
628 /*
629  * sa_zfs_set_sharenfs(group, path, on)
630  *
631  * Update the "sharenfs" property on the path. If on is true, then set
632  * to the properties on the group or "on" if no properties are
633  * defined. Set to "off" if on is false.
634  */
635 
636 int
637 sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
638 {
639 	int ret = SA_NOT_IMPLEMENTED;
640 	char *command;
641 
642 	command = malloc(ZFS_MAXPROPLEN * 2);
643 	if (command != NULL) {
644 	    char *opts = NULL;
645 	    char *dataset = NULL;
646 	    FILE *pfile;
647 	    sa_handle_impl_t impl_handle;
648 	    /* for now, NFS is always available for "zfs" */
649 	    if (on) {
650 		opts = sa_proto_legacy_format("nfs", group, 1);
651 		if (opts != NULL && strlen(opts) == 0) {
652 		    free(opts);
653 		    opts = strdup("on");
654 		}
655 	    }
656 
657 	    impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
658 	    assert(impl_handle != NULL);
659 	    if (impl_handle != NULL)
660 		dataset = get_zfs_dataset(impl_handle, path);
661 	    else
662 		ret = SA_SYSTEM_ERR;
663 
664 	    if (dataset != NULL) {
665 		(void) snprintf(command, ZFS_MAXPROPLEN * 2,
666 				"%s set sharenfs=\"%s\" %s", COMMAND,
667 				opts != NULL ? opts : "off",
668 				dataset);
669 		pfile = popen(command, "r");
670 		if (pfile != NULL) {
671 		    ret = pclose(pfile);
672 		    if (ret != 0)
673 			ret = SA_SYSTEM_ERR;
674 		}
675 	    }
676 	    if (opts != NULL)
677 		free(opts);
678 	    if (dataset != NULL)
679 		free(dataset);
680 	    free(command);
681 	}
682 	return (ret);
683 }
684 
685 /*
686  * sa_zfs_update(group)
687  *
688  * call back to ZFS to update the share if necessary.
689  * Don't do it if it isn't a real change.
690  */
691 int
692 sa_zfs_update(sa_group_t group)
693 {
694 	sa_optionset_t protopt;
695 	sa_group_t parent;
696 	char *command;
697 	char *optstring;
698 	int ret = SA_OK;
699 	int doupdate = 0;
700 	FILE *pfile;
701 
702 	if (sa_is_share(group))
703 	    parent = sa_get_parent_group(group);
704 	else
705 	    parent = group;
706 
707 	if (parent != NULL) {
708 	    command = malloc(ZFS_MAXPROPLEN * 2);
709 	    if (command == NULL)
710 		return (SA_NO_MEMORY);
711 
712 	    *command = '\0';
713 	    for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
714 		protopt = sa_get_next_optionset(protopt)) {
715 
716 		char *proto = sa_get_optionset_attr(protopt, "type");
717 		char *path;
718 		char *dataset = NULL;
719 		char *zfsopts = NULL;
720 
721 		if (sa_is_share(group)) {
722 		    path = sa_get_share_attr((sa_share_t)group, "path");
723 		    if (path != NULL) {
724 			sa_handle_impl_t impl_handle;
725 
726 			impl_handle = sa_find_group_handle(group);
727 			if (impl_handle != NULL)
728 			    dataset = get_zfs_dataset(impl_handle, path);
729 			else
730 			    ret = SA_SYSTEM_ERR;
731 
732 			sa_free_attr_string(path);
733 		    }
734 		} else {
735 		    dataset = sa_get_group_attr(group, "name");
736 		}
737 		/* update only when there is an optstring found */
738 		doupdate = 0;
739 		if (proto != NULL && dataset != NULL) {
740 		    optstring = sa_proto_legacy_format(proto, group, 1);
741 		    zfsopts = get_zfs_property(dataset, ZFS_PROP_SHARENFS);
742 
743 		    if (optstring != NULL && zfsopts != NULL) {
744 			if (strcmp(optstring, zfsopts) != 0)
745 			    doupdate++;
746 		    }
747 
748 		    if (doupdate) {
749 			if (optstring != NULL && strlen(optstring) > 0) {
750 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
751 					    "%s set sharenfs=%s %s", COMMAND,
752 					    optstring, dataset);
753 			} else {
754 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
755 					    "%s set sharenfs=on %s", COMMAND,
756 					    dataset);
757 			}
758 			pfile = popen(command, "r");
759 			if (pfile != NULL)
760 			    ret = pclose(pfile);
761 			switch (ret) {
762 			default:
763 			case 1:
764 			    ret = SA_SYSTEM_ERR;
765 			    break;
766 			case 2:
767 			    ret = SA_SYNTAX_ERR;
768 			    break;
769 			case 0:
770 			    break;
771 			}
772 		    }
773 		    if (optstring != NULL) {
774 			free(optstring);
775 		    }
776 		    if (zfsopts != NULL)
777 			free(zfsopts);
778 		}
779 		if (proto != NULL)
780 		    sa_free_attr_string(proto);
781 		if (dataset != NULL)
782 		    free(dataset);
783 	    }
784 	    free(command);
785 	}
786 	return (ret);
787 }
788 
789 /*
790  * sa_group_is_zfs(group)
791  *
792  * Given the group, determine if the zfs attribute is set.
793  */
794 
795 int
796 sa_group_is_zfs(sa_group_t group)
797 {
798 	char *zfs;
799 	int ret = 0;
800 
801 	zfs = sa_get_group_attr(group, "zfs");
802 	if (zfs != NULL) {
803 	    ret = 1;
804 	    sa_free_attr_string(zfs);
805 	}
806 	return (ret);
807 }
808 
809 /*
810  * sa_path_is_zfs(path)
811  *
812  * Check to see if the file system path represents is of type "zfs".
813  */
814 
815 int
816 sa_path_is_zfs(char *path)
817 {
818 	char *fstype;
819 	int ret = 0;
820 
821 	fstype = sa_fstype(path);
822 	if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
823 	    ret = 1;
824 	}
825 	if (fstype != NULL)
826 	    sa_free_fstype(fstype);
827 	return (ret);
828 }
829