1*3034Sdougm /*
2*3034Sdougm  * CDDL HEADER START
3*3034Sdougm  *
4*3034Sdougm  * The contents of this file are subject to the terms of the
5*3034Sdougm  * Common Development and Distribution License (the "License").
6*3034Sdougm  * You may not use this file except in compliance with the License.
7*3034Sdougm  *
8*3034Sdougm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*3034Sdougm  * or http://www.opensolaris.org/os/licensing.
10*3034Sdougm  * See the License for the specific language governing permissions
11*3034Sdougm  * and limitations under the License.
12*3034Sdougm  *
13*3034Sdougm  * When distributing Covered Code, include this CDDL HEADER in each
14*3034Sdougm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*3034Sdougm  * If applicable, add the following below this CDDL HEADER, with the
16*3034Sdougm  * fields enclosed by brackets "[]" replaced with your own identifying
17*3034Sdougm  * information: Portions Copyright [yyyy] [name of copyright owner]
18*3034Sdougm  *
19*3034Sdougm  * CDDL HEADER END
20*3034Sdougm  */
21*3034Sdougm 
22*3034Sdougm /*
23*3034Sdougm  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24*3034Sdougm  * Use is subject to license terms.
25*3034Sdougm  */
26*3034Sdougm 
27*3034Sdougm #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*3034Sdougm 
29*3034Sdougm #include "libfsmgt.h"
30*3034Sdougm #include <libzfs.h>
31*3034Sdougm #include <string.h>
32*3034Sdougm #include <libshare.h>
33*3034Sdougm #include "libshare_impl.h"
34*3034Sdougm 
35*3034Sdougm extern sa_share_t _sa_add_share(sa_group_t, char *, int, int *);
36*3034Sdougm extern sa_group_t _sa_create_zfs_group(sa_group_t, char *);
37*3034Sdougm extern char *sa_fstype(char *);
38*3034Sdougm extern void set_node_attr(void *, char *, char *);
39*3034Sdougm extern int sa_is_share(void *);
40*3034Sdougm /*
41*3034Sdougm  * File system specific code for ZFS
42*3034Sdougm  */
43*3034Sdougm 
44*3034Sdougm /*
45*3034Sdougm  * get_zfs_dataset(path)
46*3034Sdougm  *
47*3034Sdougm  * get the name of the ZFS dataset the path is equivalent to.  The
48*3034Sdougm  * dataset name is used for get/set of ZFS properties since libzfs
49*3034Sdougm  * requires a dataset to do a zfs_open().
50*3034Sdougm  */
51*3034Sdougm 
52*3034Sdougm static char *
53*3034Sdougm get_zfs_dataset(char *path)
54*3034Sdougm {
55*3034Sdougm 	fs_mntlist_t *list;
56*3034Sdougm 	fs_mntlist_t *cur;
57*3034Sdougm 	int err;
58*3034Sdougm 	char *dataset = NULL;
59*3034Sdougm 
60*3034Sdougm 	list = fs_get_filtered_mount_list(NULL, NULL, "zfs", NULL,
61*3034Sdougm 					    NULL, 0, &err);
62*3034Sdougm 	for (cur = list; cur != NULL; cur = cur->next) {
63*3034Sdougm 	    if (strcmp(path, cur->mountp) == 0 ||
64*3034Sdougm 		strncmp(path, cur->mountp, strlen(cur->mountp)) == 0) {
65*3034Sdougm 		/*
66*3034Sdougm 		 * we want the longest resource so keep trying. This
67*3034Sdougm 		 * check avoids dropping out on a partial match. ZFS
68*3034Sdougm 		 * resources are ordered when mounted in order to
69*3034Sdougm 		 * ensure inheritence of properties.
70*3034Sdougm 		 */
71*3034Sdougm 		dataset = cur->resource;
72*3034Sdougm 	    }
73*3034Sdougm 	}
74*3034Sdougm 	if (dataset != NULL) {
75*3034Sdougm 	    dataset = strdup(dataset);
76*3034Sdougm 	}
77*3034Sdougm 	fs_free_mount_list(list);
78*3034Sdougm 	return (dataset);
79*3034Sdougm }
80*3034Sdougm 
81*3034Sdougm /*
82*3034Sdougm  * get_zfs_property(dataset, property)
83*3034Sdougm  *
84*3034Sdougm  * Get the file system property specified from the ZFS dataset.
85*3034Sdougm  */
86*3034Sdougm 
87*3034Sdougm static char *
88*3034Sdougm get_zfs_property(char *dataset, zfs_prop_t property)
89*3034Sdougm {
90*3034Sdougm 	zfs_handle_t *handle = NULL;
91*3034Sdougm 	char shareopts[ZFS_MAXPROPLEN];
92*3034Sdougm 	libzfs_handle_t *libhandle;
93*3034Sdougm 
94*3034Sdougm 	libhandle = libzfs_init();
95*3034Sdougm 	if (libhandle != NULL) {
96*3034Sdougm 	    handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
97*3034Sdougm 	    if (handle != NULL) {
98*3034Sdougm 		if (zfs_prop_get(handle, property, shareopts,
99*3034Sdougm 				sizeof (shareopts), NULL, NULL, 0,
100*3034Sdougm 				FALSE) == 0) {
101*3034Sdougm 		    zfs_close(handle);
102*3034Sdougm 		    libzfs_fini(libhandle);
103*3034Sdougm 		    return (strdup(shareopts));
104*3034Sdougm 		}
105*3034Sdougm 		zfs_close(handle);
106*3034Sdougm 	    }
107*3034Sdougm 	    libzfs_fini(libhandle);
108*3034Sdougm 	}
109*3034Sdougm 	return (NULL);
110*3034Sdougm }
111*3034Sdougm 
112*3034Sdougm /*
113*3034Sdougm  * sa_zfs_is_shared(path)
114*3034Sdougm  *
115*3034Sdougm  * Check to see if the ZFS path provided has the sharenfs option set
116*3034Sdougm  * or not.
117*3034Sdougm  */
118*3034Sdougm 
119*3034Sdougm int
120*3034Sdougm sa_zfs_is_shared(char *path)
121*3034Sdougm {
122*3034Sdougm 	int ret = 0;
123*3034Sdougm 	char *dataset;
124*3034Sdougm 	zfs_handle_t *handle = NULL;
125*3034Sdougm 	char shareopts[ZFS_MAXPROPLEN];
126*3034Sdougm 	libzfs_handle_t *libhandle;
127*3034Sdougm 
128*3034Sdougm 	dataset = get_zfs_dataset(path);
129*3034Sdougm 	if (dataset != NULL) {
130*3034Sdougm 	    libhandle = libzfs_init();
131*3034Sdougm 	    if (libhandle != NULL) {
132*3034Sdougm 		handle = zfs_open(libhandle, dataset, ZFS_TYPE_FILESYSTEM);
133*3034Sdougm 		if (handle != NULL) {
134*3034Sdougm 		    if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts,
135*3034Sdougm 					sizeof (shareopts), NULL, NULL, 0,
136*3034Sdougm 					FALSE) == 0 &&
137*3034Sdougm 			strcmp(shareopts, "off") != 0)
138*3034Sdougm 			ret = 1; /* it is shared */
139*3034Sdougm 		    zfs_close(handle);
140*3034Sdougm 		}
141*3034Sdougm 		libzfs_fini(libhandle);
142*3034Sdougm 	    }
143*3034Sdougm 	    free(dataset);
144*3034Sdougm 	}
145*3034Sdougm 	return (ret);
146*3034Sdougm }
147*3034Sdougm 
148*3034Sdougm /*
149*3034Sdougm  * find_or_create_group(groupname, proto, *err)
150*3034Sdougm  *
151*3034Sdougm  * While walking the ZFS tree, we need to add shares to a defined
152*3034Sdougm  * group. If the group doesn't exist, create it first, making sure it
153*3034Sdougm  * is marked as a ZFS group.
154*3034Sdougm  *
155*3034Sdougm  * Not that all ZFS shares are in a subgroup of the top level group
156*3034Sdougm  * "zfs".
157*3034Sdougm  */
158*3034Sdougm 
159*3034Sdougm static sa_group_t
160*3034Sdougm find_or_create_group(char *groupname, char *proto, int *err)
161*3034Sdougm {
162*3034Sdougm 	sa_group_t group;
163*3034Sdougm 	sa_optionset_t optionset;
164*3034Sdougm 	int ret = SA_OK;
165*3034Sdougm 
166*3034Sdougm 	/*
167*3034Sdougm 	 * we check to see if the "zfs" group exists. Since this
168*3034Sdougm 	 * should be the top level group, we don't want the
169*3034Sdougm 	 * parent. This is to make sure the zfs group has been created
170*3034Sdougm 	 * and to created if it hasn't been.
171*3034Sdougm 	 */
172*3034Sdougm 	group = sa_get_group(groupname);
173*3034Sdougm 	if (group == NULL) {
174*3034Sdougm 	    group = sa_create_group(groupname, &ret);
175*3034Sdougm 	    if (group != NULL)
176*3034Sdougm 		ret = sa_set_group_attr(group, "zfs", "true");
177*3034Sdougm 	}
178*3034Sdougm 	if (group != NULL) {
179*3034Sdougm 	    if (proto != NULL) {
180*3034Sdougm 		optionset = sa_get_optionset(group, proto);
181*3034Sdougm 		if (optionset == NULL) {
182*3034Sdougm 		    optionset = sa_create_optionset(group, proto);
183*3034Sdougm 		} else {
184*3034Sdougm 		    char **protolist;
185*3034Sdougm 		    int numprotos, i;
186*3034Sdougm 		    numprotos = sa_get_protocols(&protolist);
187*3034Sdougm 		    for (i = 0; i < numprotos; i++) {
188*3034Sdougm 			optionset = sa_create_optionset(group, protolist[i]);
189*3034Sdougm 		    }
190*3034Sdougm 		    if (protolist != NULL)
191*3034Sdougm 			free(protolist);
192*3034Sdougm 		}
193*3034Sdougm 	    }
194*3034Sdougm 	}
195*3034Sdougm 	if (err != NULL)
196*3034Sdougm 	    *err = ret;
197*3034Sdougm 	return (group);
198*3034Sdougm }
199*3034Sdougm 
200*3034Sdougm /*
201*3034Sdougm  * sa_get_zfs_shares(groupname)
202*3034Sdougm  *
203*3034Sdougm  * Walk the mnttab for all zfs mounts and determine which are
204*3034Sdougm  * shared. Find or create the appropriate group/sub-group to contain
205*3034Sdougm  * the shares.
206*3034Sdougm  *
207*3034Sdougm  * All shares are in a sub-group that will hold the properties. This
208*3034Sdougm  * allows representing the inherited property model.
209*3034Sdougm  */
210*3034Sdougm 
211*3034Sdougm int
212*3034Sdougm sa_get_zfs_shares(char *groupname)
213*3034Sdougm {
214*3034Sdougm 	sa_group_t group;
215*3034Sdougm 	sa_group_t zfsgroup;
216*3034Sdougm 	int legacy = 0;
217*3034Sdougm 	int err;
218*3034Sdougm 	fs_mntlist_t *list;
219*3034Sdougm 	fs_mntlist_t *cur;
220*3034Sdougm 	zfs_handle_t *handle = NULL;
221*3034Sdougm 	char shareopts[ZFS_MAXPROPLEN];
222*3034Sdougm 	sa_share_t share;
223*3034Sdougm 	zfs_source_t source;
224*3034Sdougm 	char sourcestr[ZFS_MAXPROPLEN];
225*3034Sdougm 	libzfs_handle_t *libhandle;
226*3034Sdougm 
227*3034Sdougm 	/*
228*3034Sdougm 	 * if we can't access libzfs, don't bother doing anything.
229*3034Sdougm 	 */
230*3034Sdougm 	libhandle = libzfs_init();
231*3034Sdougm 	if (libhandle == NULL)
232*3034Sdougm 	    return (SA_SYSTEM_ERR);
233*3034Sdougm 
234*3034Sdougm 	zfsgroup = find_or_create_group(groupname, "nfs", &err);
235*3034Sdougm 	if (zfsgroup != NULL) {
236*3034Sdougm 		/*
237*3034Sdougm 		 * need to walk the mounted ZFS pools and datasets to
238*3034Sdougm 		 * find shares that are possible.
239*3034Sdougm 		 */
240*3034Sdougm 	    list = fs_get_filtered_mount_list(NULL, NULL, "zfs", NULL,
241*3034Sdougm 					    NULL, 0, &err);
242*3034Sdougm 	    group = zfsgroup;
243*3034Sdougm 	    for (cur = list; cur != NULL; cur = cur->next) {
244*3034Sdougm 		handle = zfs_open(libhandle, cur->resource,
245*3034Sdougm 				    ZFS_TYPE_FILESYSTEM);
246*3034Sdougm 		if (handle != NULL) {
247*3034Sdougm 		    source = ZFS_SRC_ALL;
248*3034Sdougm 		    if (zfs_prop_get(handle, ZFS_PROP_SHARENFS, shareopts,
249*3034Sdougm 					sizeof (shareopts), &source, sourcestr,
250*3034Sdougm 					ZFS_MAXPROPLEN,
251*3034Sdougm 					FALSE) == 0 &&
252*3034Sdougm 			strcmp(shareopts, "off") != 0) {
253*3034Sdougm 			/* it is shared so add to list */
254*3034Sdougm 			share = sa_find_share(cur->mountp);
255*3034Sdougm 			err = SA_OK;
256*3034Sdougm 			if (share != NULL) {
257*3034Sdougm 				/*
258*3034Sdougm 				 * A zfs file system had been shared
259*3034Sdougm 				 * through tradiditional methods
260*3034Sdougm 				 * (share/dfstab or added to a non-zfs
261*3034Sdougm 				 * group.  Now it has been added to a
262*3034Sdougm 				 * ZFS group via the zfs
263*3034Sdougm 				 * command. Remove from previous
264*3034Sdougm 				 * config and setup with current
265*3034Sdougm 				 * options.
266*3034Sdougm 				 */
267*3034Sdougm 			    err = sa_remove_share(share);
268*3034Sdougm 			    share = NULL;
269*3034Sdougm 			}
270*3034Sdougm 			if (err == SA_OK) {
271*3034Sdougm 			    if (source & ZFS_SRC_INHERITED) {
272*3034Sdougm 				share = _sa_add_share(group, cur->mountp,
273*3034Sdougm 							SA_SHARE_TRANSIENT,
274*3034Sdougm 							&err);
275*3034Sdougm 			    } else {
276*3034Sdougm 				group = _sa_create_zfs_group(zfsgroup,
277*3034Sdougm 								cur->resource);
278*3034Sdougm 				set_node_attr(group, "zfs", "true");
279*3034Sdougm 				share = _sa_add_share(group, cur->mountp,
280*3034Sdougm 							SA_SHARE_TRANSIENT,
281*3034Sdougm 							&err);
282*3034Sdougm 				if (err == SA_OK) {
283*3034Sdougm 				    char *options;
284*3034Sdougm 				    if (strcmp(shareopts, "on") != 0) {
285*3034Sdougm 					options = strdup(shareopts);
286*3034Sdougm 					if (options != NULL) {
287*3034Sdougm 					    err = sa_parse_legacy_options(group,
288*3034Sdougm 									options,
289*3034Sdougm 									"nfs");
290*3034Sdougm 					    free(options);
291*3034Sdougm 					}
292*3034Sdougm 					/* unmark the share's changed state */
293*3034Sdougm 					set_node_attr(share, "changed", NULL);
294*3034Sdougm 				    }
295*3034Sdougm 				}
296*3034Sdougm 			    }
297*3034Sdougm 			}
298*3034Sdougm 		    }
299*3034Sdougm 		}
300*3034Sdougm 	    }
301*3034Sdougm 	    if (list != NULL)
302*3034Sdougm 		fs_free_mount_list(list);
303*3034Sdougm 	}
304*3034Sdougm 	if (libhandle != NULL)
305*3034Sdougm 	    libzfs_fini(libhandle);
306*3034Sdougm 	return (legacy);
307*3034Sdougm }
308*3034Sdougm 
309*3034Sdougm #define	COMMAND		"/usr/sbin/zfs"
310*3034Sdougm 
311*3034Sdougm /*
312*3034Sdougm  * sa_zfs_set_sharenfs(group, path, on)
313*3034Sdougm  *
314*3034Sdougm  * Update the "sharenfs" property on the path. If on is true, then set
315*3034Sdougm  * to the properties on the group or "on" if no properties are
316*3034Sdougm  * defined. Set to "off" if on is false.
317*3034Sdougm  */
318*3034Sdougm 
319*3034Sdougm int
320*3034Sdougm sa_zfs_set_sharenfs(sa_group_t group, char *path, int on)
321*3034Sdougm {
322*3034Sdougm 	int ret = SA_NOT_IMPLEMENTED;
323*3034Sdougm 	char *command;
324*3034Sdougm 
325*3034Sdougm 	command = malloc(ZFS_MAXPROPLEN * 2);
326*3034Sdougm 	if (command != NULL) {
327*3034Sdougm 	    char *opts = NULL;
328*3034Sdougm 	    char *dataset;
329*3034Sdougm 	    FILE *pfile;
330*3034Sdougm 	    /* for now, NFS is always available for "zfs" */
331*3034Sdougm 	    if (on) {
332*3034Sdougm 		opts = sa_proto_legacy_format("nfs", group, 1);
333*3034Sdougm 		if (opts != NULL && strlen(opts) == 0) {
334*3034Sdougm 		    free(opts);
335*3034Sdougm 		    opts = strdup("on");
336*3034Sdougm 		}
337*3034Sdougm 	    }
338*3034Sdougm 	    dataset = get_zfs_dataset(path);
339*3034Sdougm 	    if (dataset != NULL) {
340*3034Sdougm 		(void) snprintf(command, ZFS_MAXPROPLEN * 2,
341*3034Sdougm 				"%s set sharenfs=\"%s\" %s", COMMAND,
342*3034Sdougm 				opts != NULL ? opts : "off",
343*3034Sdougm 				dataset);
344*3034Sdougm 		pfile = popen(command, "r");
345*3034Sdougm 		if (pfile != NULL) {
346*3034Sdougm 		    ret = pclose(pfile);
347*3034Sdougm 		    if (ret != 0)
348*3034Sdougm 			ret = SA_SYSTEM_ERR;
349*3034Sdougm 		}
350*3034Sdougm 	    }
351*3034Sdougm 	    if (opts != NULL)
352*3034Sdougm 		free(opts);
353*3034Sdougm 	    if (dataset != NULL)
354*3034Sdougm 		free(dataset);
355*3034Sdougm 	    free(command);
356*3034Sdougm 	}
357*3034Sdougm 	return (ret);
358*3034Sdougm }
359*3034Sdougm 
360*3034Sdougm /*
361*3034Sdougm  * sa_zfs_update(group)
362*3034Sdougm  *
363*3034Sdougm  * call back to ZFS to update the share if necessary.
364*3034Sdougm  * Don't do it if it isn't a real change.
365*3034Sdougm  */
366*3034Sdougm int
367*3034Sdougm sa_zfs_update(sa_group_t group)
368*3034Sdougm {
369*3034Sdougm 	sa_optionset_t protopt;
370*3034Sdougm 	sa_group_t parent;
371*3034Sdougm 	char *command;
372*3034Sdougm 	char *optstring;
373*3034Sdougm 	int ret = SA_OK;
374*3034Sdougm 	int doupdate = 0;
375*3034Sdougm 	FILE *pfile;
376*3034Sdougm 
377*3034Sdougm 	if (sa_is_share(group))
378*3034Sdougm 	    parent = sa_get_parent_group(group);
379*3034Sdougm 	else
380*3034Sdougm 	    parent = group;
381*3034Sdougm 
382*3034Sdougm 	if (parent != NULL) {
383*3034Sdougm 	    command = malloc(ZFS_MAXPROPLEN * 2);
384*3034Sdougm 	    if (command == NULL)
385*3034Sdougm 		return (SA_NO_MEMORY);
386*3034Sdougm 
387*3034Sdougm 	    *command = '\0';
388*3034Sdougm 	    for (protopt = sa_get_optionset(parent, NULL); protopt != NULL;
389*3034Sdougm 		protopt = sa_get_next_optionset(protopt)) {
390*3034Sdougm 
391*3034Sdougm 		char *proto = sa_get_optionset_attr(protopt, "type");
392*3034Sdougm 		char *path;
393*3034Sdougm 		char *dataset = NULL;
394*3034Sdougm 		char *zfsopts = NULL;
395*3034Sdougm 
396*3034Sdougm 		if (sa_is_share(group)) {
397*3034Sdougm 		    path = sa_get_share_attr((sa_share_t)group, "path");
398*3034Sdougm 		    if (path != NULL) {
399*3034Sdougm 			dataset = get_zfs_dataset(path);
400*3034Sdougm 			sa_free_attr_string(path);
401*3034Sdougm 		    }
402*3034Sdougm 		} else {
403*3034Sdougm 		    dataset = sa_get_group_attr(group, "name");
404*3034Sdougm 		}
405*3034Sdougm 		/* update only when there is an optstring found */
406*3034Sdougm 		doupdate = 0;
407*3034Sdougm 		if (proto != NULL && dataset != NULL) {
408*3034Sdougm 		    optstring = sa_proto_legacy_format(proto, group, 1);
409*3034Sdougm 		    zfsopts = get_zfs_property(dataset, ZFS_PROP_SHARENFS);
410*3034Sdougm 
411*3034Sdougm 		    if (optstring != NULL && zfsopts != NULL) {
412*3034Sdougm 			if (strcmp(optstring, zfsopts) != 0)
413*3034Sdougm 			    doupdate++;
414*3034Sdougm 		    }
415*3034Sdougm 
416*3034Sdougm 		    if (doupdate) {
417*3034Sdougm 			if (optstring != NULL && strlen(optstring) > 0) {
418*3034Sdougm 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
419*3034Sdougm 					    "%s set sharenfs=%s %s", COMMAND,
420*3034Sdougm 					    optstring, dataset);
421*3034Sdougm 			} else {
422*3034Sdougm 			    (void) snprintf(command, ZFS_MAXPROPLEN * 2,
423*3034Sdougm 					    "%s set sharenfs=on %s", COMMAND,
424*3034Sdougm 					    dataset);
425*3034Sdougm 			}
426*3034Sdougm 			pfile = popen(command, "r");
427*3034Sdougm 			if (pfile != NULL)
428*3034Sdougm 			    ret = pclose(pfile);
429*3034Sdougm 			switch (ret) {
430*3034Sdougm 			default:
431*3034Sdougm 			case 1:
432*3034Sdougm 			    ret = SA_SYSTEM_ERR;
433*3034Sdougm 			    break;
434*3034Sdougm 			case 2:
435*3034Sdougm 			    ret = SA_SYNTAX_ERR;
436*3034Sdougm 			    break;
437*3034Sdougm 			case 0:
438*3034Sdougm 			    break;
439*3034Sdougm 			}
440*3034Sdougm 		    }
441*3034Sdougm 		    if (optstring != NULL) {
442*3034Sdougm 			free(optstring);
443*3034Sdougm 		    }
444*3034Sdougm 		    if (zfsopts != NULL)
445*3034Sdougm 			free(zfsopts);
446*3034Sdougm 		}
447*3034Sdougm 		if (proto != NULL)
448*3034Sdougm 		    sa_free_attr_string(proto);
449*3034Sdougm 		if (dataset != NULL)
450*3034Sdougm 		    free(dataset);
451*3034Sdougm 	    }
452*3034Sdougm 	    free(command);
453*3034Sdougm 	}
454*3034Sdougm 	return (ret);
455*3034Sdougm }
456*3034Sdougm 
457*3034Sdougm /*
458*3034Sdougm  * sa_group_is_zfs(group)
459*3034Sdougm  *
460*3034Sdougm  * Given the group, determine if the zfs attribute is set.
461*3034Sdougm  */
462*3034Sdougm 
463*3034Sdougm int
464*3034Sdougm sa_group_is_zfs(sa_group_t group)
465*3034Sdougm {
466*3034Sdougm 	char *zfs;
467*3034Sdougm 	int ret = 0;
468*3034Sdougm 
469*3034Sdougm 	zfs = sa_get_group_attr(group, "zfs");
470*3034Sdougm 	if (zfs != NULL) {
471*3034Sdougm 	    ret = 1;
472*3034Sdougm 	    sa_free_attr_string(zfs);
473*3034Sdougm 	}
474*3034Sdougm 	return (ret);
475*3034Sdougm }
476*3034Sdougm 
477*3034Sdougm /*
478*3034Sdougm  * sa_path_is_zfs(path)
479*3034Sdougm  *
480*3034Sdougm  * Check to see if the file system path represents is of type "zfs".
481*3034Sdougm  */
482*3034Sdougm 
483*3034Sdougm int
484*3034Sdougm sa_path_is_zfs(char *path)
485*3034Sdougm {
486*3034Sdougm 	char *fstype;
487*3034Sdougm 	int ret = 0;
488*3034Sdougm 
489*3034Sdougm 	fstype = sa_fstype(path);
490*3034Sdougm 	if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
491*3034Sdougm 	    ret = 1;
492*3034Sdougm 	}
493*3034Sdougm 	if (fstype != NULL)
494*3034Sdougm 	    sa_free_fstype(fstype);
495*3034Sdougm 	return (ret);
496*3034Sdougm }
497