xref: /titanic_41/usr/src/lib/libshare/common/libshare_zfs.c (revision 4aac33d31b41cc7e3ac6fb66747ff2cae63d08cf)
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 
208 	get_all_filesystems(impl_handle, &zlist, &count);
209 	qsort(zlist, count, sizeof (void *), mountpoint_compare);
210 	for (i = 0; i < count; i++) {
211 	    /* must have a mountpoint */
212 	    if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint,
213 		sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) {
214 		/* no mountpoint */
215 		continue;
216 	    }
217 
218 	    /* mountpoint must be a path */
219 	    if (strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) == 0 ||
220 		strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0)
221 		continue;
222 
223 	    /* canmount must be set */
224 	    if (!zfs_prop_get_int(zlist[i], ZFS_PROP_CANMOUNT))
225 		continue;
226 
227 	/*
228 	 * have a mountable handle but want to skip those marked none
229 	 * and legacy
230 	 */
231 	    if (strcmp(mountpoint, path) == 0) {
232 		dataset = (char *)zfs_get_name(zlist[i]);
233 		break;
234 	    }
235 
236 	}
237 
238 	if (dataset != NULL) {
239 	    dataset = strdup(dataset);
240 	}
241 	return (dataset);
242 }
243 
244 /*
245  * get_zfs_property(dataset, property)
246  *
247  * Get the file system property specified from the ZFS dataset.
248  */
249 
250 static char *
251 get_zfs_property(char *dataset, zfs_prop_t property)
252 {
253 	zfs_handle_t *handle = NULL;
254 	char shareopts[ZFS_MAXPROPLEN];
255 	libzfs_handle_t *libhandle;
256 
257 	libhandle = libzfs_init();
258 	if (libhandle != NULL) {
259 	    handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
260 	    if (handle != NULL) {
261 		if (zfs_prop_get(handle, property, shareopts,
262 				sizeof (shareopts), NULL, NULL, 0,
263 				B_FALSE) == 0) {
264 		    zfs_close(handle);
265 		    libzfs_fini(libhandle);
266 		    return (strdup(shareopts));
267 		}
268 		zfs_close(handle);
269 	    }
270 	    libzfs_fini(libhandle);
271 	}
272 	return (NULL);
273 }
274 
275 /*
276  * sa_zfs_is_shared(handle, path)
277  *
278  * Check to see if the ZFS path provided has the sharenfs option set
279  * or not.
280  */
281 
282 int
283 sa_zfs_is_shared(sa_handle_t sahandle, char *path)
284 {
285 	int ret = 0;
286 	char *dataset;
287 	zfs_handle_t *handle = NULL;
288 	char shareopts[ZFS_MAXPROPLEN];
289 	libzfs_handle_t *libhandle;
290 
291 	dataset = get_zfs_dataset((sa_handle_t)sahandle, path);
292 	if (dataset != NULL) {
293 	    libhandle = libzfs_init();
294 	    if (libhandle != NULL) {
295 		handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
296 		if (handle != NULL) {
297 		    if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts,
298 					sizeof (shareopts), NULL, NULL, 0,
299 					B_FALSE) == 0 &&
300 			strcmp(shareopts, "off") != 0)
301 			ret = 1; /* it is shared */
302 		    zfs_close(handle);
303 		}
304 		libzfs_fini(libhandle);
305 	    }
306 	    free(dataset);
307 	}
308 	return (ret);
309 }
310 
311 /*
312  * find_or_create_group(groupname, proto, *err)
313  *
314  * While walking the ZFS tree, we need to add shares to a defined
315  * group. If the group doesn't exist, create it first, making sure it
316  * is marked as a ZFS group.
317  *
318  * Note that all ZFS shares are in a subgroup of the top level group
319  * called "zfs".
320  */
321 
322 static sa_group_t
323 find_or_create_group(sa_handle_t handle, char *groupname, char *proto, int *err)
324 {
325 	sa_group_t group;
326 	sa_optionset_t optionset;
327 	int ret = SA_OK;
328 
329 	/*
330 	 * we check to see if the "zfs" group exists. Since this
331 	 * should be the top level group, we don't want the
332 	 * parent. This is to make sure the zfs group has been created
333 	 * and to created if it hasn't been.
334 	 */
335 	group = sa_get_group(handle, groupname);
336 	if (group == NULL) {
337 	    group = sa_create_group(handle, groupname, &ret);
338 
339 	    /* make sure this is flagged as a ZFS group */
340 	    if (group != NULL)
341 		ret = sa_set_group_attr(group, "zfs", "true");
342 	}
343 	if (group != NULL) {
344 	    if (proto != NULL) {
345 		optionset = sa_get_optionset(group, proto);
346 		if (optionset == NULL) {
347 		    optionset = sa_create_optionset(group, proto);
348 		} else {
349 		    char **protolist;
350 		    int numprotos, i;
351 		    numprotos = sa_get_protocols(&protolist);
352 		    for (i = 0; i < numprotos; i++) {
353 			optionset = sa_create_optionset(group, protolist[i]);
354 		    }
355 		    if (protolist != NULL)
356 			free(protolist);
357 		}
358 	    }
359 	}
360 	if (err != NULL)
361 	    *err = ret;
362 	return (group);
363 }
364 
365 /*
366  * find_or_create_zfs_subgroup(groupname, optstring, *err)
367  *
368  * ZFS shares will be in a subgroup of the "zfs" master group.  This
369  * function looks to see if the groupname exists and returns it if it
370  * does or else creates a new one with the specified name and returns
371  * that.  The "zfs" group will exist before we get here, but we make
372  * sure just in case.
373  *
374  * err must be a valid pointer.
375  */
376 
377 static sa_group_t
378 find_or_create_zfs_subgroup(sa_handle_t handle, char *groupname,
379 				char *optstring, int *err)
380 {
381 	sa_group_t group = NULL;
382 	sa_group_t zfs;
383 	char *name;
384 	char *options;
385 
386 	/* start with the top-level "zfs" group */
387 	zfs = sa_get_group(handle, "zfs");
388 	*err = SA_OK;
389 	if (zfs != NULL) {
390 	    for (group = sa_get_sub_group(zfs); group != NULL;
391 		group = sa_get_next_group(group)) {
392 		name = sa_get_group_attr(group, "name");
393 		if (name != NULL && strcmp(name, groupname) == 0) {
394 		    /* have the group so break out of here */
395 		    sa_free_attr_string(name);
396 		    break;
397 		}
398 		if (name != NULL)
399 		    sa_free_attr_string(name);
400 	    }
401 
402 	    if (group == NULL) {
403 		/* need to create the sub-group since it doesn't exist */
404 		group = _sa_create_zfs_group(zfs, groupname);
405 		if (group != NULL) {
406 		    set_node_attr(group, "zfs", "true");
407 		}
408 		if (strcmp(optstring, "on") == 0)
409 		    optstring = "rw";
410 		if (group != NULL) {
411 		    options = strdup(optstring);
412 		    if (options != NULL) {
413 			*err = sa_parse_legacy_options(group, options, "nfs");
414 			free(options);
415 		    } else {
416 			*err = SA_NO_MEMORY;
417 		    }
418 		}
419 	    }
420 	}
421 	return (group);
422 }
423 
424 /*
425  * sa_get_zfs_shares(handle, groupname)
426  *
427  * Walk the mnttab for all zfs mounts and determine which are
428  * shared. Find or create the appropriate group/sub-group to contain
429  * the shares.
430  *
431  * All shares are in a sub-group that will hold the properties. This
432  * allows representing the inherited property model.
433  */
434 
435 int
436 sa_get_zfs_shares(sa_handle_t handle, char *groupname)
437 {
438 	sa_group_t group;
439 	sa_group_t zfsgroup;
440 	int legacy = 0;
441 	int err;
442 	zfs_handle_t **zlist;
443 	char shareopts[ZFS_MAXPROPLEN];
444 	sa_share_t share;
445 	zfs_source_t source;
446 	char sourcestr[ZFS_MAXPROPLEN];
447 	char mountpoint[ZFS_MAXPROPLEN];
448 	char *options;
449 	size_t count = 0, i;
450 	libzfs_handle_t *zfs_libhandle;
451 
452 	/*
453 	 * If we can't access libzfs, don't bother doing anything.
454 	 */
455 	zfs_libhandle = ((sa_handle_impl_t)handle)->zfs_libhandle;
456 	if (zfs_libhandle == NULL)
457 	    return (SA_SYSTEM_ERR);
458 
459 	zfsgroup = find_or_create_group(handle, groupname, "nfs", &err);
460 	if (zfsgroup != NULL) {
461 		/*
462 		 * need to walk the mounted ZFS pools and datasets to
463 		 * find shares that are possible.
464 		 */
465 	    get_all_filesystems((sa_handle_impl_t)handle, &zlist, &count);
466 	    qsort(zlist, count, sizeof (void *), mountpoint_compare);
467 
468 	    group = zfsgroup;
469 	    for (i = 0; i < count; i++) {
470 		char *dataset;
471 
472 		source = ZFS_SRC_ALL;
473 		if (zfs_prop_get(zlist[i], ZFS_PROP_MOUNTPOINT, mountpoint,
474 					sizeof (mountpoint), NULL, NULL, 0,
475 					B_FALSE) != 0) {
476 		    /* no mountpoint */
477 		    continue;
478 		}
479 
480 		/*
481 		 * zfs_get_name value must not be freed. It is just a
482 		 * pointer to a value in the handle.
483 		 */
484 		if ((dataset = (char *)zfs_get_name(zlist[i])) == NULL)
485 		    continue;
486 
487 		/*
488 		 * only deal with "mounted" file systems since
489 		 * unmounted file systems can't actually be shared.
490 		 */
491 
492 		if (!zfs_is_mounted(zlist[i], NULL))
493 		    continue;
494 
495 		if (zfs_prop_get(zlist[i], ZFS_PROP_SHARENFS, shareopts,
496 					sizeof (shareopts), &source, sourcestr,
497 					ZFS_MAXPROPLEN,
498 					B_FALSE) == 0 &&
499 			strcmp(shareopts, "off") != 0) {
500 		    /* it is shared so add to list */
501 		    share = sa_find_share(handle, mountpoint);
502 		    err = SA_OK;
503 		    if (share != NULL) {
504 			/*
505 			 * A zfs file system had been shared
506 			 * through traditional methods
507 			 * (share/dfstab or added to a non-zfs
508 			 * group.  Now it has been added to a
509 			 * ZFS group via the zfs
510 			 * command. Remove from previous
511 			 * config and setup with current
512 			 * options.
513 			 */
514 			err = sa_remove_share(share);
515 			share = NULL;
516 		    }
517 		    if (err == SA_OK) {
518 			if (source & ZFS_SRC_INHERITED) {
519 			    int doshopt = 0;
520 			/*
521 			 * Need to find the "real" parent
522 			 * sub-group. It may not be mounted, but it
523 			 * was identified in the "sourcestr"
524 			 * variable. The real parent not mounted can
525 			 * occur if "canmount=off and sharenfs=on".
526 			 */
527 			    group = find_or_create_zfs_subgroup(handle,
528 							sourcestr,
529 							shareopts, &doshopt);
530 			    if (group != NULL) {
531 				share = _sa_add_share(group, mountpoint,
532 							SA_SHARE_TRANSIENT,
533 							&err);
534 				/*
535 				 * some options may only be on
536 				 * shares. If the opt string
537 				 * contains one of those, we
538 				 * put it just on the share.
539 				 */
540 				if (share != NULL &&
541 				    doshopt == SA_PROP_SHARE_ONLY) {
542 				    options = strdup(shareopts);
543 				    if (options != NULL) {
544 					err = sa_parse_legacy_options(share,
545 								options, "nfs");
546 					free(options);
547 				    }
548 				}
549 			    } else {
550 				err = SA_NO_MEMORY;
551 			    }
552 			} else {
553 			    group = _sa_create_zfs_group(zfsgroup, dataset);
554 			    if (group == NULL) {
555 				static int err = 0;
556 				/*
557 				 * there is a problem, but we can't do
558 				 * anything about it at this point so
559 				 * we issue a warning an move on.
560 				 */
561 				if (err == 0) {
562 				    /* only print error once */
563 				    (void) fprintf(stderr,
564 					dgettext(TEXT_DOMAIN,
565 						"Cannot create ZFS subgroup "
566 						"during initialization:"
567 						" %s\n"),
568 					sa_errorstr(SA_SYSTEM_ERR));
569 				    err = 1;
570 				}
571 				continue;
572 			    }
573 			    set_node_attr(group, "zfs", "true");
574 			    share = _sa_add_share(group, mountpoint,
575 						SA_SHARE_TRANSIENT, &err);
576 			    if (err == SA_OK) {
577 				if (strcmp(shareopts, "on") != 0) {
578 				    options = strdup(shareopts);
579 				    if (options != NULL) {
580 					err = sa_parse_legacy_options(group,
581 									options,
582 									"nfs");
583 					free(options);
584 				    }
585 				    if (err == SA_PROP_SHARE_ONLY) {
586 					/*
587 					 * Same as above, some
588 					 * properties may only be on
589 					 * shares, but due to the ZFS
590 					 * sub-groups being
591 					 * artificial, we sometimes
592 					 * get this and have to deal
593 					 * with it. We do it by
594 					 * attempting to put it on the
595 					 * share.
596 					 */
597 					options = strdup(shareopts);
598 					if (options != NULL)
599 					    err = sa_parse_legacy_options(
600 									share,
601 									options,
602 									"nfs");
603 					    free(options);
604 					}
605 				    /* unmark the share's changed state */
606 				    set_node_attr(share, "changed", NULL);
607 				}
608 			    }
609 			}
610 		    }
611 		}
612 	    }
613 	}
614 	/*
615 	 * Don't need to free the "zlist" variable since it is only a
616 	 * pointer to a cached value that will be freed when
617 	 * sa_fini() is called.
618 	 */
619 	return (legacy);
620 }
621 
622 #define	COMMAND		"/usr/sbin/zfs"
623 
624 /*
625  * sa_zfs_set_sharenfs(group, path, on)
626  *
627  * Update the "sharenfs" property on the path. If on is true, then set
628  * to the properties on the group or "on" if no properties are
629  * defined. Set to "off" if on is false.
630  */
631 
632 int
633 sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
634 {
635 	int ret = SA_NOT_IMPLEMENTED;
636 	char *command;
637 
638 	command = malloc(ZFS_MAXPROPLEN * 2);
639 	if (command != NULL) {
640 	    char *opts = NULL;
641 	    char *dataset = NULL;
642 	    FILE *pfile;
643 	    sa_handle_impl_t impl_handle;
644 	    /* for now, NFS is always available for "zfs" */
645 	    if (on) {
646 		opts = sa_proto_legacy_format("nfs", group, 1);
647 		if (opts != NULL && strlen(opts) == 0) {
648 		    free(opts);
649 		    opts = strdup("on");
650 		}
651 	    }
652 
653 	    impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
654 	    if (impl_handle != NULL)
655 		dataset = get_zfs_dataset(impl_handle, path);
656 	    else
657 		ret = SA_SYSTEM_ERR;
658 
659 	    if (dataset != NULL) {
660 		(void) snprintf(command, ZFS_MAXPROPLEN * 2,
661 				"%s set sharenfs=\"%s\" %s", COMMAND,
662 				opts != NULL ? opts : "off",
663 				dataset);
664 		pfile = popen(command, "r");
665 		if (pfile != NULL) {
666 		    ret = pclose(pfile);
667 		    if (ret != 0)
668 			ret = SA_SYSTEM_ERR;
669 		}
670 	    }
671 	    if (opts != NULL)
672 		free(opts);
673 	    if (dataset != NULL)
674 		free(dataset);
675 	    free(command);
676 	}
677 	return (ret);
678 }
679 
680 /*
681  * sa_zfs_update(group)
682  *
683  * call back to ZFS to update the share if necessary.
684  * Don't do it if it isn't a real change.
685  */
686 int
687 sa_zfs_update(sa_group_t group)
688 {
689 	sa_optionset_t protopt;
690 	sa_group_t parent;
691 	char *command;
692 	char *optstring;
693 	int ret = SA_OK;
694 	int doupdate = 0;
695 	FILE *pfile;
696 
697 	if (sa_is_share(group))
698 	    parent = sa_get_parent_group(group);
699 	else
700 	    parent = group;
701 
702 	if (parent != NULL) {
703 	    command = malloc(ZFS_MAXPROPLEN * 2);
704 	    if (command == NULL)
705 		return (SA_NO_MEMORY);
706 
707 	    *command = '\0';
708 	    for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
709 		protopt = sa_get_next_optionset(protopt)) {
710 
711 		char *proto = sa_get_optionset_attr(protopt, "type");
712 		char *path;
713 		char *dataset = NULL;
714 		char *zfsopts = NULL;
715 
716 		if (sa_is_share(group)) {
717 		    path = sa_get_share_attr((sa_share_t)group, "path");
718 		    if (path != NULL) {
719 			sa_handle_impl_t impl_handle;
720 
721 			impl_handle = sa_find_group_handle(group);
722 			if (impl_handle != NULL)
723 			    dataset = get_zfs_dataset(impl_handle, path);
724 			else
725 			    ret = SA_SYSTEM_ERR;
726 
727 			sa_free_attr_string(path);
728 		    }
729 		} else {
730 		    dataset = sa_get_group_attr(group, "name");
731 		}
732 		/* update only when there is an optstring found */
733 		doupdate = 0;
734 		if (proto != NULL && dataset != NULL) {
735 		    optstring = sa_proto_legacy_format(proto, group, 1);
736 		    zfsopts = get_zfs_property(dataset, ZFS_PROP_SHARENFS);
737 
738 		    if (optstring != NULL && zfsopts != NULL) {
739 			if (strcmp(optstring, zfsopts) != 0)
740 			    doupdate++;
741 		    }
742 
743 		    if (doupdate) {
744 			if (optstring != NULL && strlen(optstring) > 0) {
745 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
746 					    "%s set sharenfs=%s %s", COMMAND,
747 					    optstring, dataset);
748 			} else {
749 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
750 					    "%s set sharenfs=on %s", COMMAND,
751 					    dataset);
752 			}
753 			pfile = popen(command, "r");
754 			if (pfile != NULL)
755 			    ret = pclose(pfile);
756 			switch (ret) {
757 			default:
758 			case 1:
759 			    ret = SA_SYSTEM_ERR;
760 			    break;
761 			case 2:
762 			    ret = SA_SYNTAX_ERR;
763 			    break;
764 			case 0:
765 			    break;
766 			}
767 		    }
768 		    if (optstring != NULL) {
769 			free(optstring);
770 		    }
771 		    if (zfsopts != NULL)
772 			free(zfsopts);
773 		}
774 		if (proto != NULL)
775 		    sa_free_attr_string(proto);
776 		if (dataset != NULL)
777 		    free(dataset);
778 	    }
779 	    free(command);
780 	}
781 	return (ret);
782 }
783 
784 /*
785  * sa_group_is_zfs(group)
786  *
787  * Given the group, determine if the zfs attribute is set.
788  */
789 
790 int
791 sa_group_is_zfs(sa_group_t group)
792 {
793 	char *zfs;
794 	int ret = 0;
795 
796 	zfs = sa_get_group_attr(group, "zfs");
797 	if (zfs != NULL) {
798 	    ret = 1;
799 	    sa_free_attr_string(zfs);
800 	}
801 	return (ret);
802 }
803 
804 /*
805  * sa_path_is_zfs(path)
806  *
807  * Check to see if the file system path represents is of type "zfs".
808  */
809 
810 int
811 sa_path_is_zfs(char *path)
812 {
813 	char *fstype;
814 	int ret = 0;
815 
816 	fstype = sa_fstype(path);
817 	if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
818 	    ret = 1;
819 	}
820 	if (fstype != NULL)
821 	    sa_free_fstype(fstype);
822 	return (ret);
823 }
824