16185db85Sdougm /*
26185db85Sdougm  * CDDL HEADER START
36185db85Sdougm  *
46185db85Sdougm  * The contents of this file are subject to the terms of the
56185db85Sdougm  * Common Development and Distribution License (the "License").
66185db85Sdougm  * You may not use this file except in compliance with the License.
76185db85Sdougm  *
86185db85Sdougm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96185db85Sdougm  * or http://www.opensolaris.org/os/licensing.
106185db85Sdougm  * See the License for the specific language governing permissions
116185db85Sdougm  * and limitations under the License.
126185db85Sdougm  *
136185db85Sdougm  * When distributing Covered Code, include this CDDL HEADER in each
146185db85Sdougm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156185db85Sdougm  * If applicable, add the following below this CDDL HEADER, with the
166185db85Sdougm  * fields enclosed by brackets "[]" replaced with your own identifying
176185db85Sdougm  * information: Portions Copyright [yyyy] [name of copyright owner]
186185db85Sdougm  *
196185db85Sdougm  * CDDL HEADER END
206185db85Sdougm  */
216185db85Sdougm 
226185db85Sdougm /*
23148c5f43SAlan Wright  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24*b8dc1b43SJoyce McIntosh  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
258a981c33SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
266185db85Sdougm  */
276185db85Sdougm 
286185db85Sdougm /*
296185db85Sdougm  * Share control API
306185db85Sdougm  */
316185db85Sdougm #include <stdio.h>
326185db85Sdougm #include <string.h>
336185db85Sdougm #include <ctype.h>
346185db85Sdougm #include <sys/types.h>
356185db85Sdougm #include <sys/stat.h>
36a99982a7Sdougm #include <fcntl.h>
376185db85Sdougm #include <unistd.h>
386185db85Sdougm #include <libxml/parser.h>
396185db85Sdougm #include <libxml/tree.h>
406185db85Sdougm #include "libshare.h"
416185db85Sdougm #include "libshare_impl.h"
426185db85Sdougm #include <libscf.h>
436185db85Sdougm #include "scfutil.h"
446185db85Sdougm #include <ctype.h>
456185db85Sdougm #include <libintl.h>
46549ec3ffSdougm #include <thread.h>
47549ec3ffSdougm #include <synch.h>
48015408caSJean McCormack #include <errno.h>
496185db85Sdougm 
50a99982a7Sdougm #define	DFS_LOCK_FILE	"/etc/dfs/fstypes"
5157b448deSdougm #define	SA_STRSIZE	256	/* max string size for names */
52a99982a7Sdougm 
53da6c28aaSamw /*
54da6c28aaSamw  * internal object type values returned by sa_get_object_type()
55da6c28aaSamw  */
56da6c28aaSamw #define	SA_TYPE_UNKNOWN		0
57da6c28aaSamw #define	SA_TYPE_GROUP		1
58da6c28aaSamw #define	SA_TYPE_SHARE		2
59da6c28aaSamw #define	SA_TYPE_RESOURCE	3
60da6c28aaSamw #define	SA_TYPE_OPTIONSET	4
61da6c28aaSamw #define	SA_TYPE_ALTSPACE	5
62da6c28aaSamw 
636185db85Sdougm /*
646185db85Sdougm  * internal data structures
656185db85Sdougm  */
666185db85Sdougm 
676185db85Sdougm extern struct sa_proto_plugin *sap_proto_list;
686185db85Sdougm 
696185db85Sdougm /* current SMF/SVC repository handle */
70549ec3ffSdougm extern void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *);
71549ec3ffSdougm extern int gettransients(sa_handle_impl_t, xmlNodePtr *);
728a981c33SDaniel Hoffman extern int get_one_transient(sa_handle_impl_t, xmlNodePtr *, char **, size_t);
736185db85Sdougm extern char *sa_fstype(char *);
746185db85Sdougm extern int sa_is_share(void *);
75da6c28aaSamw extern int sa_is_resource(void *);
766185db85Sdougm extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */
776185db85Sdougm extern int sa_group_is_zfs(sa_group_t);
786185db85Sdougm extern int sa_path_is_zfs(char *);
796185db85Sdougm extern int sa_zfs_set_sharenfs(sa_group_t, char *, int);
80da6c28aaSamw extern int sa_zfs_set_sharesmb(sa_group_t, char *, int);
81549ec3ffSdougm extern void update_legacy_config(sa_handle_t);
826185db85Sdougm extern int issubdir(char *, char *);
8357b448deSdougm extern int sa_zfs_init(sa_handle_impl_t);
84549ec3ffSdougm extern void sa_zfs_fini(sa_handle_impl_t);
85a99982a7Sdougm extern void sablocksigs(sigset_t *);
86a99982a7Sdougm extern void saunblocksigs(sigset_t *);
87da6c28aaSamw static sa_group_t sa_get_optionset_parent(sa_optionset_t);
88da6c28aaSamw static char *get_node_attr(void *, char *);
895b6e0c46Sdougm extern void sa_update_sharetab_ts(sa_handle_t);
906185db85Sdougm 
91549ec3ffSdougm /*
92549ec3ffSdougm  * Data structures for finding/managing the document root to access
93549ec3ffSdougm  * handle mapping. The list isn't expected to grow very large so a
94549ec3ffSdougm  * simple list is acceptable. The purpose is to provide a way to start
95549ec3ffSdougm  * with a group or share and find the library handle needed for
96549ec3ffSdougm  * various operations.
97549ec3ffSdougm  */
98549ec3ffSdougm mutex_t sa_global_lock;
99549ec3ffSdougm struct doc2handle {
100549ec3ffSdougm 	struct doc2handle	*next;
101549ec3ffSdougm 	xmlNodePtr		root;
102549ec3ffSdougm 	sa_handle_impl_t	handle;
103549ec3ffSdougm };
104549ec3ffSdougm 
105148c5f43SAlan Wright mutex_t sa_dfstab_lock;
106148c5f43SAlan Wright 
10757b448deSdougm /* definitions used in a couple of property functions */
10857b448deSdougm #define	SA_PROP_OP_REMOVE	1
10957b448deSdougm #define	SA_PROP_OP_ADD		2
11057b448deSdougm #define	SA_PROP_OP_UPDATE	3
11157b448deSdougm 
112549ec3ffSdougm static struct doc2handle *sa_global_handles = NULL;
1136185db85Sdougm 
1146185db85Sdougm /* helper functions */
1156185db85Sdougm 
116549ec3ffSdougm /*
117549ec3ffSdougm  * sa_errorstr(err)
118549ec3ffSdougm  *
119549ec3ffSdougm  * convert an error value to an error string
120549ec3ffSdougm  */
121549ec3ffSdougm 
1226185db85Sdougm char *
sa_errorstr(int err)1236185db85Sdougm sa_errorstr(int err)
1246185db85Sdougm {
1256185db85Sdougm 	static char errstr[32];
1266185db85Sdougm 	char *ret = NULL;
1276185db85Sdougm 
1286185db85Sdougm 	switch (err) {
1296185db85Sdougm 	case SA_OK:
13057b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "ok");
13157b448deSdougm 		break;
1326185db85Sdougm 	case SA_NO_SUCH_PATH:
13357b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "path doesn't exist");
13457b448deSdougm 		break;
1356185db85Sdougm 	case SA_NO_MEMORY:
13657b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "no memory");
13757b448deSdougm 		break;
1386185db85Sdougm 	case SA_DUPLICATE_NAME:
13957b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "name in use");
14057b448deSdougm 		break;
1416185db85Sdougm 	case SA_BAD_PATH:
14257b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "bad path");
14357b448deSdougm 		break;
1446185db85Sdougm 	case SA_NO_SUCH_GROUP:
14557b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "no such group");
14657b448deSdougm 		break;
1476185db85Sdougm 	case SA_CONFIG_ERR:
14857b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "configuration error");
14957b448deSdougm 		break;
1506185db85Sdougm 	case SA_SYSTEM_ERR:
15157b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "system error");
15257b448deSdougm 		break;
1536185db85Sdougm 	case SA_SYNTAX_ERR:
15457b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "syntax error");
15557b448deSdougm 		break;
1566185db85Sdougm 	case SA_NO_PERMISSION:
15757b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "no permission");
15857b448deSdougm 		break;
1596185db85Sdougm 	case SA_BUSY:
16057b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "busy");
16157b448deSdougm 		break;
1626185db85Sdougm 	case SA_NO_SUCH_PROP:
16357b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "no such property");
16457b448deSdougm 		break;
1656185db85Sdougm 	case SA_INVALID_NAME:
16657b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "invalid name");
16757b448deSdougm 		break;
1686185db85Sdougm 	case SA_INVALID_PROTOCOL:
16957b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "invalid protocol");
17057b448deSdougm 		break;
1716185db85Sdougm 	case SA_NOT_ALLOWED:
17257b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "operation not allowed");
17357b448deSdougm 		break;
1746185db85Sdougm 	case SA_BAD_VALUE:
17557b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "bad property value");
17657b448deSdougm 		break;
1776185db85Sdougm 	case SA_INVALID_SECURITY:
17857b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "invalid security type");
17957b448deSdougm 		break;
1806185db85Sdougm 	case SA_NO_SUCH_SECURITY:
18157b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "security type not found");
18257b448deSdougm 		break;
1836185db85Sdougm 	case SA_VALUE_CONFLICT:
18457b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "property value conflict");
18557b448deSdougm 		break;
1866185db85Sdougm 	case SA_NOT_IMPLEMENTED:
18757b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "not implemented");
18857b448deSdougm 		break;
1896185db85Sdougm 	case SA_INVALID_PATH:
19057b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "invalid path");
19157b448deSdougm 		break;
1926185db85Sdougm 	case SA_NOT_SUPPORTED:
19357b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "operation not supported");
19457b448deSdougm 		break;
1956185db85Sdougm 	case SA_PROP_SHARE_ONLY:
19657b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "property not valid for group");
19757b448deSdougm 		break;
1986185db85Sdougm 	case SA_NOT_SHARED:
19957b448deSdougm 		ret = dgettext(TEXT_DOMAIN, "not shared");
20057b448deSdougm 		break;
201da6c28aaSamw 	case SA_NO_SUCH_RESOURCE:
202da6c28aaSamw 		ret = dgettext(TEXT_DOMAIN, "no such resource");
203da6c28aaSamw 		break;
204da6c28aaSamw 	case SA_RESOURCE_REQUIRED:
205da6c28aaSamw 		ret = dgettext(TEXT_DOMAIN, "resource name required");
206da6c28aaSamw 		break;
207da6c28aaSamw 	case SA_MULTIPLE_ERROR:
208da6c28aaSamw 		ret = dgettext(TEXT_DOMAIN, "errors from multiple protocols");
209da6c28aaSamw 		break;
210da6c28aaSamw 	case SA_PATH_IS_SUBDIR:
211da6c28aaSamw 		ret = dgettext(TEXT_DOMAIN, "path is a subpath of share");
212da6c28aaSamw 		break;
213da6c28aaSamw 	case SA_PATH_IS_PARENTDIR:
214da6c28aaSamw 		ret = dgettext(TEXT_DOMAIN, "path is parent of a share");
215da6c28aaSamw 		break;
2164bff34e3Sthurlow 	case SA_NO_SECTION:
2174bff34e3Sthurlow 		ret = dgettext(TEXT_DOMAIN, "protocol requires a section");
2184bff34e3Sthurlow 		break;
2194bff34e3Sthurlow 	case SA_NO_PROPERTIES:
2204bff34e3Sthurlow 		ret = dgettext(TEXT_DOMAIN, "properties not found");
2214bff34e3Sthurlow 		break;
2224bff34e3Sthurlow 	case SA_NO_SUCH_SECTION:
2234bff34e3Sthurlow 		ret = dgettext(TEXT_DOMAIN, "section not found");
2244bff34e3Sthurlow 		break;
2254bff34e3Sthurlow 	case SA_PASSWORD_ENC:
2264bff34e3Sthurlow 		ret = dgettext(TEXT_DOMAIN, "passwords must be encrypted");
2274bff34e3Sthurlow 		break;
2280616fd7fSPavel Filipensky 	case SA_SHARE_EXISTS:
2290616fd7fSPavel Filipensky 		ret = dgettext(TEXT_DOMAIN, "path or file is already shared");
2300616fd7fSPavel Filipensky 		break;
2316185db85Sdougm 	default:
23257b448deSdougm 		(void) snprintf(errstr, sizeof (errstr),
23357b448deSdougm 		    dgettext(TEXT_DOMAIN, "unknown %d"), err);
23457b448deSdougm 		ret = errstr;
2356185db85Sdougm 	}
2366185db85Sdougm 	return (ret);
2376185db85Sdougm }
2386185db85Sdougm 
239549ec3ffSdougm /*
240549ec3ffSdougm  * Document root to active handle mapping functions.  These are only
241549ec3ffSdougm  * used internally. A mutex is used to prevent access while the list
242549ec3ffSdougm  * is changing. In general, the list will be relatively short - one
243549ec3ffSdougm  * item per thread that has called sa_init().
244549ec3ffSdougm  */
245549ec3ffSdougm 
246549ec3ffSdougm sa_handle_impl_t
get_handle_for_root(xmlNodePtr root)247549ec3ffSdougm get_handle_for_root(xmlNodePtr root)
248549ec3ffSdougm {
249549ec3ffSdougm 	struct doc2handle *item;
250549ec3ffSdougm 
251549ec3ffSdougm 	(void) mutex_lock(&sa_global_lock);
252549ec3ffSdougm 	for (item = sa_global_handles; item != NULL; item = item->next) {
25357b448deSdougm 		if (item->root == root)
25457b448deSdougm 			break;
255549ec3ffSdougm 	}
256549ec3ffSdougm 	(void) mutex_unlock(&sa_global_lock);
257549ec3ffSdougm 	if (item != NULL)
25857b448deSdougm 		return (item->handle);
259549ec3ffSdougm 	return (NULL);
260549ec3ffSdougm }
261549ec3ffSdougm 
262549ec3ffSdougm static int
add_handle_for_root(xmlNodePtr root,sa_handle_impl_t handle)263549ec3ffSdougm add_handle_for_root(xmlNodePtr root, sa_handle_impl_t handle)
264549ec3ffSdougm {
265549ec3ffSdougm 	struct doc2handle *item;
266549ec3ffSdougm 	int ret = SA_NO_MEMORY;
267549ec3ffSdougm 
268549ec3ffSdougm 	item = (struct doc2handle *)calloc(sizeof (struct doc2handle), 1);
269549ec3ffSdougm 	if (item != NULL) {
27057b448deSdougm 		item->root = root;
27157b448deSdougm 		item->handle = handle;
27257b448deSdougm 		(void) mutex_lock(&sa_global_lock);
27357b448deSdougm 		item->next = sa_global_handles;
27457b448deSdougm 		sa_global_handles = item;
27557b448deSdougm 		(void) mutex_unlock(&sa_global_lock);
27657b448deSdougm 		ret = SA_OK;
277549ec3ffSdougm 	}
278549ec3ffSdougm 	return (ret);
279549ec3ffSdougm }
280549ec3ffSdougm 
281549ec3ffSdougm /*
282549ec3ffSdougm  * remove_handle_for_root(root)
283549ec3ffSdougm  *
284549ec3ffSdougm  * Walks the list of handles and removes the one for this "root" from
285549ec3ffSdougm  * the list. It is up to the caller to free the data.
286549ec3ffSdougm  */
287549ec3ffSdougm 
288549ec3ffSdougm static void
remove_handle_for_root(xmlNodePtr root)289549ec3ffSdougm remove_handle_for_root(xmlNodePtr root)
290549ec3ffSdougm {
291549ec3ffSdougm 	struct doc2handle *item, *prev;
292549ec3ffSdougm 
293549ec3ffSdougm 	(void) mutex_lock(&sa_global_lock);
294549ec3ffSdougm 	for (prev = NULL, item = sa_global_handles; item != NULL;
29557b448deSdougm 	    item = item->next) {
29657b448deSdougm 		if (item->root == root) {
29757b448deSdougm 			/* first in the list */
29857b448deSdougm 			if (prev == NULL)
29957b448deSdougm 				sa_global_handles = sa_global_handles->next;
30057b448deSdougm 			else
30157b448deSdougm 				prev->next = item->next;
30257b448deSdougm 			/* Item is out of the list so free the list structure */
30357b448deSdougm 			free(item);
30457b448deSdougm 			break;
305549ec3ffSdougm 		}
30657b448deSdougm 		prev = item;
307549ec3ffSdougm 	}
308549ec3ffSdougm 	(void) mutex_unlock(&sa_global_lock);
309549ec3ffSdougm }
310549ec3ffSdougm 
311549ec3ffSdougm /*
312549ec3ffSdougm  * sa_find_group_handle(sa_group_t group)
313549ec3ffSdougm  *
314549ec3ffSdougm  * Find the sa_handle_t for the configuration associated with this
315549ec3ffSdougm  * group.
316549ec3ffSdougm  */
317549ec3ffSdougm sa_handle_t
sa_find_group_handle(sa_group_t group)318549ec3ffSdougm sa_find_group_handle(sa_group_t group)
319549ec3ffSdougm {
320549ec3ffSdougm 	xmlNodePtr node = (xmlNodePtr)group;
321549ec3ffSdougm 	sa_handle_t handle;
322549ec3ffSdougm 
323549ec3ffSdougm 	while (node != NULL) {
32457b448deSdougm 		if (strcmp((char *)(node->name), "sharecfg") == 0) {
32557b448deSdougm 			/* have the root so get the handle */
32657b448deSdougm 			handle = (sa_handle_t)get_handle_for_root(node);
32757b448deSdougm 			return (handle);
32857b448deSdougm 		}
32957b448deSdougm 		node = node->parent;
330549ec3ffSdougm 	}
331549ec3ffSdougm 	return (NULL);
332549ec3ffSdougm }
333549ec3ffSdougm 
3346185db85Sdougm /*
3356185db85Sdougm  * set_legacy_timestamp(root, path, timevalue)
3366185db85Sdougm  *
3376185db85Sdougm  * add the current timestamp value to the configuration for use in
3386185db85Sdougm  * determining when to update the legacy files.  For SMF, this
3396185db85Sdougm  * property is kept in default/operation/legacy_timestamp
3406185db85Sdougm  */
3416185db85Sdougm 
3426185db85Sdougm static void
set_legacy_timestamp(xmlNodePtr root,char * path,uint64_t tval)3436185db85Sdougm set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval)
3446185db85Sdougm {
3456185db85Sdougm 	xmlNodePtr node;
3466185db85Sdougm 	xmlChar *lpath = NULL;
347549ec3ffSdougm 	sa_handle_impl_t handle;
348549ec3ffSdougm 
349549ec3ffSdougm 	/* Have to have a handle or else we weren't initialized. */
350549ec3ffSdougm 	handle = get_handle_for_root(root);
351549ec3ffSdougm 	if (handle == NULL)
35257b448deSdougm 		return;
3536185db85Sdougm 
3546185db85Sdougm 	for (node = root->xmlChildrenNode; node != NULL;
35557b448deSdougm 	    node = node->next) {
35657b448deSdougm 		if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) {
35757b448deSdougm 			/* a possible legacy node for this path */
35857b448deSdougm 			lpath = xmlGetProp(node, (xmlChar *)"path");
35957b448deSdougm 			if (lpath != NULL &&
36057b448deSdougm 			    xmlStrcmp(lpath, (xmlChar *)path) == 0) {
36157b448deSdougm 				xmlFree(lpath);
36257b448deSdougm 				break;
36357b448deSdougm 			}
36457b448deSdougm 			if (lpath != NULL)
36557b448deSdougm 				xmlFree(lpath);
3666185db85Sdougm 		}
3676185db85Sdougm 	}
3686185db85Sdougm 	if (node == NULL) {
36957b448deSdougm 		/* need to create the first legacy timestamp node */
37057b448deSdougm 		node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL);
3716185db85Sdougm 	}
3726185db85Sdougm 	if (node != NULL) {
37357b448deSdougm 		char tstring[32];
37457b448deSdougm 		int ret;
37557b448deSdougm 
37657b448deSdougm 		(void) snprintf(tstring, sizeof (tstring), "%lld", tval);
3774bff34e3Sthurlow 		(void) xmlSetProp(node, (xmlChar *)"timestamp",
3784bff34e3Sthurlow 		    (xmlChar *)tstring);
3794bff34e3Sthurlow 		(void) xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path);
38057b448deSdougm 		/* now commit to SMF */
38157b448deSdougm 		ret = sa_get_instance(handle->scfhandle, "default");
3826185db85Sdougm 		if (ret == SA_OK) {
38357b448deSdougm 			ret = sa_start_transaction(handle->scfhandle,
38457b448deSdougm 			    "operation");
38557b448deSdougm 			if (ret == SA_OK) {
38657b448deSdougm 				ret = sa_set_property(handle->scfhandle,
38757b448deSdougm 				    "legacy-timestamp", tstring);
38857b448deSdougm 				if (ret == SA_OK) {
38957b448deSdougm 					(void) sa_end_transaction(
3905b6e0c46Sdougm 					    handle->scfhandle, handle);
39157b448deSdougm 				} else {
39257b448deSdougm 					sa_abort_transaction(handle->scfhandle);
39357b448deSdougm 				}
39457b448deSdougm 			}
3956185db85Sdougm 		}
3966185db85Sdougm 	}
3976185db85Sdougm }
3986185db85Sdougm 
3996185db85Sdougm /*
4006185db85Sdougm  * is_shared(share)
4016185db85Sdougm  *
4026185db85Sdougm  * determine if the specified share is currently shared or not.
4036185db85Sdougm  */
4046185db85Sdougm static int
is_shared(sa_share_t share)4056185db85Sdougm is_shared(sa_share_t share)
4066185db85Sdougm {
4076185db85Sdougm 	char *shared;
4086185db85Sdougm 	int result = 0; /* assume not */
4096185db85Sdougm 
4106185db85Sdougm 	shared = sa_get_share_attr(share, "shared");
4116185db85Sdougm 	if (shared != NULL) {
41257b448deSdougm 		if (strcmp(shared, "true") == 0)
41357b448deSdougm 			result = 1;
41457b448deSdougm 		sa_free_attr_string(shared);
4156185db85Sdougm 	}
4166185db85Sdougm 	return (result);
4176185db85Sdougm }
4186185db85Sdougm 
419da6c28aaSamw /*
420da6c28aaSamw  * excluded_protocol(share, proto)
421da6c28aaSamw  *
422da6c28aaSamw  * Returns B_TRUE if the specified protocol appears in the "exclude"
423da6c28aaSamw  * property. This is used to prevent sharing special case shares
424da6c28aaSamw  * (e.g. subdirs when SMB wants a subdir and NFS doesn't. B_FALSE is
425da6c28aaSamw  * returned if the protocol isn't in the list.
426da6c28aaSamw  */
427da6c28aaSamw static boolean_t
excluded_protocol(sa_share_t share,char * proto)428da6c28aaSamw excluded_protocol(sa_share_t share, char *proto)
429da6c28aaSamw {
430da6c28aaSamw 	char *protolist;
431da6c28aaSamw 	char *str;
432da6c28aaSamw 	char *token;
433da6c28aaSamw 
434da6c28aaSamw 	protolist = sa_get_share_attr(share, "exclude");
435da6c28aaSamw 	if (protolist != NULL) {
436da6c28aaSamw 		str = protolist;
437da6c28aaSamw 		while ((token = strtok(str, ",")) != NULL) {
438da6c28aaSamw 			if (strcmp(token, proto) == 0) {
439da6c28aaSamw 				sa_free_attr_string(protolist);
440da6c28aaSamw 				return (B_TRUE);
441