xref: /illumos-gate/usr/src/lib/libshare/common/libshare.c (revision e9610e3e86cbfb5fd6797a438c65b493250b4219)
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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24   * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
25   * Copyright (c) 2016 by Delphix. All rights reserved.
26   */
27  
28  /*
29   * Share control API
30   */
31  #include <stdio.h>
32  #include <string.h>
33  #include <ctype.h>
34  #include <sys/types.h>
35  #include <sys/stat.h>
36  #include <fcntl.h>
37  #include <unistd.h>
38  #include <libxml/parser.h>
39  #include <libxml/tree.h>
40  #include "libshare.h"
41  #include "libshare_impl.h"
42  #include <libscf.h>
43  #include "scfutil.h"
44  #include <ctype.h>
45  #include <libintl.h>
46  #include <thread.h>
47  #include <synch.h>
48  #include <errno.h>
49  
50  #define	DFS_LOCK_FILE	"/etc/dfs/fstypes"
51  #define	SA_STRSIZE	256	/* max string size for names */
52  
53  /*
54   * internal object type values returned by sa_get_object_type()
55   */
56  #define	SA_TYPE_UNKNOWN		0
57  #define	SA_TYPE_GROUP		1
58  #define	SA_TYPE_SHARE		2
59  #define	SA_TYPE_RESOURCE	3
60  #define	SA_TYPE_OPTIONSET	4
61  #define	SA_TYPE_ALTSPACE	5
62  
63  /*
64   * internal data structures
65   */
66  
67  extern struct sa_proto_plugin *sap_proto_list;
68  
69  /* current SMF/SVC repository handle */
70  extern void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *);
71  extern int gettransients(sa_handle_impl_t, xmlNodePtr *);
72  extern int get_one_transient(sa_handle_impl_t, xmlNodePtr *, char **, size_t);
73  extern char *sa_fstype(char *);
74  extern int sa_is_share(void *);
75  extern int sa_is_resource(void *);
76  extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */
77  extern int sa_group_is_zfs(sa_group_t);
78  extern int sa_path_is_zfs(char *);
79  extern int sa_zfs_set_sharenfs(sa_group_t, char *, int);
80  extern int sa_zfs_set_sharesmb(sa_group_t, char *, int);
81  extern void update_legacy_config(sa_handle_t);
82  extern int issubdir(char *, char *);
83  extern int sa_zfs_init(sa_handle_impl_t);
84  extern void sa_zfs_fini(sa_handle_impl_t);
85  extern void sablocksigs(sigset_t *);
86  extern void saunblocksigs(sigset_t *);
87  static sa_group_t sa_get_optionset_parent(sa_optionset_t);
88  static char *get_node_attr(void *, char *);
89  extern void sa_update_sharetab_ts(sa_handle_t);
90  
91  /*
92   * Data structures for finding/managing the document root to access
93   * handle mapping. The list isn't expected to grow very large so a
94   * simple list is acceptable. The purpose is to provide a way to start
95   * with a group or share and find the library handle needed for
96   * various operations.
97   */
98  mutex_t sa_global_lock;
99  struct doc2handle {
100  	struct doc2handle	*next;
101  	xmlNodePtr		root;
102  	sa_handle_impl_t	handle;
103  };
104  
105  mutex_t sa_dfstab_lock;
106  
107  /* definitions used in a couple of property functions */
108  #define	SA_PROP_OP_REMOVE	1
109  #define	SA_PROP_OP_ADD		2
110  #define	SA_PROP_OP_UPDATE	3
111  
112  static struct doc2handle *sa_global_handles = NULL;
113  
114  /* helper functions */
115  
116  /*
117   * sa_errorstr(err)
118   *
119   * convert an error value to an error string
120   */
121  
122  char *
123  sa_errorstr(int err)
124  {
125  	static char errstr[32];
126  	char *ret = NULL;
127  
128  	switch (err) {
129  	case SA_OK:
130  		ret = dgettext(TEXT_DOMAIN, "ok");
131  		break;
132  	case SA_NO_SUCH_PATH:
133  		ret = dgettext(TEXT_DOMAIN, "path doesn't exist");
134  		break;
135  	case SA_NO_MEMORY:
136  		ret = dgettext(TEXT_DOMAIN, "no memory");
137  		break;
138  	case SA_DUPLICATE_NAME:
139  		ret = dgettext(TEXT_DOMAIN, "name in use");
140  		break;
141  	case SA_BAD_PATH:
142  		ret = dgettext(TEXT_DOMAIN, "bad path");
143  		break;
144  	case SA_NO_SUCH_GROUP:
145  		ret = dgettext(TEXT_DOMAIN, "no such group");
146  		break;
147  	case SA_CONFIG_ERR:
148  		ret = dgettext(TEXT_DOMAIN, "configuration error");
149  		break;
150  	case SA_SYSTEM_ERR:
151  		ret = dgettext(TEXT_DOMAIN, "system error");
152  		break;
153  	case SA_SYNTAX_ERR:
154  		ret = dgettext(TEXT_DOMAIN, "syntax error");
155  		break;
156  	case SA_NO_PERMISSION:
157  		ret = dgettext(TEXT_DOMAIN, "no permission");
158  		break;
159  	case SA_BUSY:
160  		ret = dgettext(TEXT_DOMAIN, "busy");
161  		break;
162  	case SA_NO_SUCH_PROP:
163  		ret = dgettext(TEXT_DOMAIN, "no such property");
164  		break;
165  	case SA_INVALID_NAME:
166  		ret = dgettext(TEXT_DOMAIN, "invalid name");
167  		break;
168  	case SA_INVALID_PROTOCOL:
169  		ret = dgettext(TEXT_DOMAIN, "invalid protocol");
170  		break;
171  	case SA_NOT_ALLOWED:
172  		ret = dgettext(TEXT_DOMAIN, "operation not allowed");
173  		break;
174  	case SA_BAD_VALUE:
175  		ret = dgettext(TEXT_DOMAIN, "bad property value");
176  		break;
177  	case SA_INVALID_SECURITY:
178  		ret = dgettext(TEXT_DOMAIN, "invalid security type");
179  		break;
180  	case SA_NO_SUCH_SECURITY:
181  		ret = dgettext(TEXT_DOMAIN, "security type not found");
182  		break;
183  	case SA_VALUE_CONFLICT:
184  		ret = dgettext(TEXT_DOMAIN, "property value conflict");
185  		break;
186  	case SA_NOT_IMPLEMENTED:
187  		ret = dgettext(TEXT_DOMAIN, "not implemented");
188  		break;
189  	case SA_INVALID_PATH:
190  		ret = dgettext(TEXT_DOMAIN, "invalid path");
191  		break;
192  	case SA_NOT_SUPPORTED:
193  		ret = dgettext(TEXT_DOMAIN, "operation not supported");
194  		break;
195  	case SA_PROP_SHARE_ONLY:
196  		ret = dgettext(TEXT_DOMAIN, "property not valid for group");
197  		break;
198  	case SA_NOT_SHARED:
199  		ret = dgettext(TEXT_DOMAIN, "not shared");
200  		break;
201  	case SA_NO_SUCH_RESOURCE:
202  		ret = dgettext(TEXT_DOMAIN, "no such resource");
203  		break;
204  	case SA_RESOURCE_REQUIRED:
205  		ret = dgettext(TEXT_DOMAIN, "resource name required");
206  		break;
207  	case SA_MULTIPLE_ERROR:
208  		ret = dgettext(TEXT_DOMAIN, "errors from multiple protocols");
209  		break;
210  	case SA_PATH_IS_SUBDIR:
211  		ret = dgettext(TEXT_DOMAIN, "path is a subpath of share");
212  		break;
213  	case SA_PATH_IS_PARENTDIR:
214  		ret = dgettext(TEXT_DOMAIN, "path is parent of a share");
215  		break;
216  	case SA_NO_SECTION:
217  		ret = dgettext(TEXT_DOMAIN, "protocol requires a section");
218  		break;
219  	case SA_NO_PROPERTIES:
220  		ret = dgettext(TEXT_DOMAIN, "properties not found");
221  		break;
222  	case SA_NO_SUCH_SECTION:
223  		ret = dgettext(TEXT_DOMAIN, "section not found");
224  		break;
225  	case SA_PASSWORD_ENC:
226  		ret = dgettext(TEXT_DOMAIN, "passwords must be encrypted");
227  		break;
228  	case SA_SHARE_EXISTS:
229  		ret = dgettext(TEXT_DOMAIN, "path or file is already shared");
230  		break;
231  	default:
232  		(void) snprintf(errstr, sizeof (errstr),
233  		    dgettext(TEXT_DOMAIN, "unknown %d"), err);
234  		ret = errstr;
235  	}
236  	return (ret);
237  }
238  
239  /*
240   * Document root to active handle mapping functions.  These are only
241   * used internally. A mutex is used to prevent access while the list
242   * is changing. In general, the list will be relatively short - one
243   * item per thread that has called sa_init().
244   */
245  
246  sa_handle_impl_t
247  get_handle_for_root(xmlNodePtr root)
248  {
249  	struct doc2handle *item;
250  
251  	(void) mutex_lock(&sa_global_lock);
252  	for (item = sa_global_handles; item != NULL; item = item->next) {
253  		if (item->root == root)
254  			break;
255  	}
256  	(void) mutex_unlock(&sa_global_lock);
257  	if (item != NULL)
258  		return (item->handle);
259  	return (NULL);
260  }
261  
262  static int
263  add_handle_for_root(xmlNodePtr root, sa_handle_impl_t handle)
264  {
265  	struct doc2handle *item;
266  	int ret = SA_NO_MEMORY;
267  
268  	item = (struct doc2handle *)calloc(sizeof (struct doc2handle), 1);
269  	if (item != NULL) {
270  		item->root = root;
271  		item->handle = handle;
272  		(void) mutex_lock(&sa_global_lock);
273  		item->next = sa_global_handles;
274  		sa_global_handles = item;
275  		(void) mutex_unlock(&sa_global_lock);
276  		ret = SA_OK;
277  	}
278  	return (ret);
279  }
280  
281  /*
282   * remove_handle_for_root(root)
283   *
284   * Walks the list of handles and removes the one for this "root" from
285   * the list. It is up to the caller to free the data.
286   */
287  
288  static void
289  remove_handle_for_root(xmlNodePtr root)
290  {
291  	struct doc2handle *item, *prev;
292  
293  	(void) mutex_lock(&sa_global_lock);
294  	for (prev = NULL, item = sa_global_handles; item != NULL;
295  	    item = item->next) {
296  		if (item->root == root) {
297  			/* first in the list */
298  			if (prev == NULL)
299  				sa_global_handles = sa_global_handles->next;
300  			else
301  				prev->next = item->next;
302  			/* Item is out of the list so free the list structure */
303  			free(item);
304  			break;
305  		}
306  		prev = item;
307  	}
308  	(void) mutex_unlock(&sa_global_lock);
309  }
310  
311  /*
312   * sa_find_group_handle(sa_group_t group)
313   *
314   * Find the sa_handle_t for the configuration associated with this
315   * group.
316   */
317  sa_handle_t
318  sa_find_group_handle(sa_group_t group)
319  {
320  	xmlNodePtr node = (xmlNodePtr)group;
321  	sa_handle_t handle;
322  
323  	while (node != NULL) {
324  		if (strcmp((char *)(node->name), "sharecfg") == 0) {
325  			/* have the root so get the handle */
326  			handle = (sa_handle_t)get_handle_for_root(node);
327  			return (handle);
328  		}
329  		node = node->parent;
330  	}
331  	return (NULL);
332  }
333  
334  /*
335   * set_legacy_timestamp(root, path, timevalue)
336   *
337   * add the current timestamp value to the configuration for use in
338   * determining when to update the legacy files.  For SMF, this
339   * property is kept in default/operation/legacy_timestamp
340   */
341  
342  static void
343  set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval)
344  {
345  	xmlNodePtr node;
346  	xmlChar *lpath = NULL;
347  	sa_handle_impl_t handle;
348  
349  	/* Have to have a handle or else we weren't initialized. */
350  	handle = get_handle_for_root(root);
351  	if (handle == NULL)
352  		return;
353  
354  	for (node = root->xmlChildrenNode; node != NULL;
355  	    node = node->next) {
356  		if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) {
357  			/* a possible legacy node for this path */
358  			lpath = xmlGetProp(node, (xmlChar *)"path");
359  			if (lpath != NULL &&
360  			    xmlStrcmp(lpath, (xmlChar *)path) == 0) {
361  				xmlFree(lpath);
362  				break;
363  			}
364  			if (lpath != NULL)
365  				xmlFree(lpath);
366  		}
367  	}
368  	if (node == NULL) {
369  		/* need to create the first legacy timestamp node */
370  		node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL);
371  	}
372  	if (node != NULL) {
373  		char tstring[32];
374  		int ret;
375  
376  		(void) snprintf(tstring, sizeof (tstring), "%lld", tval);
377  		(void) xmlSetProp(node, (xmlChar *)"timestamp",
378  		    (xmlChar *)tstring);
379  		(void) xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path);
380  		/* now commit to SMF */
381  		ret = sa_get_instance(handle->scfhandle, "default");
382  		if (ret == SA_OK) {
383  			ret = sa_start_transaction(handle->scfhandle,
384  			    "operation");
385  			if (ret == SA_OK) {
386  				ret = sa_set_property(handle->scfhandle,
387  				    "legacy-timestamp", tstring);
388  				if (ret == SA_OK) {
389  					(void) sa_end_transaction(
390  					    handle->scfhandle, handle);
391  				} else {
392  					sa_abort_transaction(handle->scfhandle);
393  				}
394  			}
395  		}
396  	}
397  }
398  
399  /*
400   * is_shared(share)
401   *
402   * determine if the specified share is currently shared or not.
403   */
404  static int
405  is_shared(sa_share_t share)
406  {
407  	char *shared;
408  	int result = 0; /* assume not */
409  
410  	shared = sa_get_share_attr(share, "shared");
411  	if (shared != NULL) {
412  		if (strcmp(shared, "true") == 0)
413  			result = 1;
414  		sa_free_attr_string(shared);
415  	}
416  	return (result);
417  }
418  
419  /*
420   * excluded_protocol(share, proto)
421   *
422   * Returns B_TRUE if the specified protocol appears in the "exclude"
423   * property. This is used to prevent sharing special case shares
424   * (e.g. subdirs when SMB wants a subdir and NFS doesn't. B_FALSE is
425   * returned if the protocol isn't in the list.
426   */
427  static boolean_t
428  excluded_protocol(sa_share_t share, char *proto)
429  {
430  	char *protolist;
431  	char *str;
432  	char *token;
433  
434  	protolist = sa_get_share_attr(share, "exclude");
435  	if (protolist != NULL) {
436  		str = protolist;
437  		while ((token = strtok(str, ",")) != NULL) {
438  			if (strcmp(token, proto) == 0) {
439  				sa_free_attr_string(protolist);
440  				return (B_TRUE);
441  			}
442  			str = NULL;
443  		}
444  		sa_free_attr_string(protolist);
445  	}
446  	return (B_FALSE);
447  }
448  
449  /*
450   * checksubdirgroup(group, newpath, strictness)
451   *
452   * check all the specified newpath against all the paths in the
453   * group. This is a helper function for checksubdir to make it easier
454   * to also check ZFS subgroups.
455   * The strictness values mean:
456   * SA_CHECK_NORMAL == only check newpath against shares that are active
457   * SA_CHECK_STRICT == check newpath against both active shares and those
458   *		      stored in the repository
459   */
460  static int
461  checksubdirgroup(sa_group_t group, char *newpath, int strictness)
462  {
463  	sa_share_t share;
464  	char *path;
465  	int issub = SA_OK;
466  	int subdir;
467  	int parent;
468  
469  	if (newpath == NULL)
470  		return (SA_INVALID_PATH);
471  
472  	for (share = sa_get_share(group, NULL); share != NULL;
473  	    share = sa_get_next_share(share)) {
474  		/*
475  		 * The original behavior of share never checked
476  		 * against the permanent configuration
477  		 * (/etc/dfs/dfstab).  PIT has a number of cases where
478  		 * it depends on this older behavior even though it
479  		 * could be considered incorrect.  We may tighten this
480  		 * up in the future.
481  		 */
482  		if (strictness == SA_CHECK_NORMAL && !is_shared(share))
483  			continue;
484  
485  		path = sa_get_share_attr(share, "path");
486  		/*
487  		 * If path is NULL, then a share is in the process of
488  		 * construction or someone has modified the property
489  		 * group inappropriately. It should be
490  		 * ignored. issubdir() comes from the original share
491  		 * implementation and does the difficult part of
492  		 * checking subdirectories.
493  		 */
494  		if (path == NULL)
495  			continue;
496  
497  		if (strcmp(path, newpath) == 0) {
498  			issub = SA_INVALID_PATH;
499  		} else {
500  			subdir = issubdir(newpath, path);
501  			parent = issubdir(path, newpath);
502  			if (subdir || parent) {
503  				sa_free_attr_string(path);
504  				path = NULL;
505  				return (subdir ?
506  				    SA_PATH_IS_SUBDIR : SA_PATH_IS_PARENTDIR);
507  			}
508  		}
509  		sa_free_attr_string(path);
510  		path = NULL;
511  	}
512  	return (issub);
513  }
514  
515  /*
516   * checksubdir(newpath, strictness)
517   *
518   * checksubdir determines if the specified path (newpath) is a
519   * subdirectory of another share. It calls checksubdirgroup() to do
520   * the complicated work. The strictness parameter determines how
521   * strict a check to make against the path. The strictness values
522   * mean: SA_CHECK_NORMAL == only check newpath against shares that are
523   * active SA_CHECK_STRICT == check newpath against both active shares
524   * and those * stored in the repository
525   */
526  static int
527  checksubdir(sa_handle_t handle, char *newpath, int strictness)
528  {
529  	sa_group_t group;
530  	int issub = SA_OK;
531  	char *path = NULL;
532  
533  	for (group = sa_get_group(handle, NULL);
534  	    group != NULL && issub == SA_OK;
535  	    group = sa_get_next_group(group)) {
536  		if (sa_group_is_zfs(group)) {
537  			sa_group_t subgroup;
538  			for (subgroup = sa_get_sub_group(group);
539  			    subgroup != NULL && issub == SA_OK;
540  			    subgroup = sa_get_next_group(subgroup))
541  				issub = checksubdirgroup(subgroup, newpath,
542  				    strictness);
543  		} else {
544  			issub = checksubdirgroup(group, newpath, strictness);
545  		}
546  	}
547  	if (path != NULL)
548  		sa_free_attr_string(path);
549  	return (issub);
550  }
551  
552  /*
553   * validpath(path, strictness)
554   * determine if the provided path is valid for a share. It shouldn't
555   * be a sub-dir of an already shared path or the parent directory of a
556   * share path.
557   */
558  static int
559  validpath(sa_handle_t handle, char *path, int strictness)
560  {
561  	int error = SA_OK;
562  	struct stat st;
563  	sa_share_t share;
564  	char *fstype;
565  
566  	if (*path != '/')
567  		return (SA_BAD_PATH);
568  
569  	if (stat(path, &st) < 0) {
570  		error = SA_NO_SUCH_PATH;
571  	} else {
572  		share = sa_find_share(handle, path);
573  		if (share != NULL)
574  			error = SA_DUPLICATE_NAME;
575  
576  		if (error == SA_OK) {
577  			/*
578  			 * check for special case with file system
579  			 * that might have restrictions.  For now, ZFS
580  			 * is the only case since it has its own idea
581  			 * of how to configure shares. We do this
582  			 * before subdir checking since things like
583  			 * ZFS will do that for us. This should also
584  			 * be done via plugin interface.
585  			 */
586  			fstype = sa_fstype(path);
587  			if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
588  				if (sa_zfs_is_shared(handle, path))
589  					error = SA_INVALID_NAME;
590  			}
591  			if (fstype != NULL)
592  				sa_free_fstype(fstype);
593  		}
594  		if (error == SA_OK)
595  			error = checksubdir(handle, path, strictness);
596  	}
597  	return (error);
598  }
599  
600  /*
601   * check to see if group/share is persistent.
602   *
603   * "group" can be either an sa_group_t or an sa_share_t. (void *)
604   * works since both these types are also void *.
605   * If the share is a ZFS share, mark it as persistent.
606   */
607  int
608  sa_is_persistent(void *group)
609  {
610  	char *type;
611  	int persist = 1;
612  	sa_group_t grp;
613  
614  	type = sa_get_group_attr((sa_group_t)group, "type");
615  	if (type != NULL) {
616  		if (strcmp(type, "transient") == 0)
617  			persist = 0;
618  		sa_free_attr_string(type);
619  	}
620  
621  	grp = (sa_is_share(group)) ? sa_get_parent_group(group) : group;
622  	if (sa_group_is_zfs(grp))
623  		persist = 1;
624  
625  	return (persist);
626  }
627  
628  /*
629   * sa_valid_group_name(name)
630   *
631   * check that the "name" contains only valid characters and otherwise
632   * fits the required naming conventions. Valid names must start with
633   * an alphabetic and the remainder may consist of only alphanumeric
634   * plus the '-' and '_' characters. This name limitation comes from
635   * inherent limitations in SMF.
636   */
637  
638  int
639  sa_valid_group_name(char *name)
640  {
641  	int ret = 1;
642  	ssize_t len;
643  
644  	if (name != NULL && isalpha(*name)) {
645  		char c;
646  		len = strlen(name);
647  		if (len < (scf_max_name_len - sizeof ("group:"))) {
648  			for (c = *name++; c != '\0' && ret != 0; c = *name++) {
649  				if (!isalnum(c) && c != '-' && c != '_')
650  					ret = 0;
651  			}
652  		} else {
653  			ret = 0;
654  		}
655  	} else {
656  		ret = 0;
657  	}
658  	return (ret);
659  }
660  
661  
662  /*
663   * is_zfs_group(group)
664   *	Determine if the specified group is a ZFS sharenfs group
665   */
666  static int
667  is_zfs_group(sa_group_t group)
668  {
669  	int ret = 0;
670  	xmlNodePtr parent;
671  	xmlChar *zfs;
672  
673  	if (strcmp((char *)((xmlNodePtr)group)->name, "share") == 0)
674  		parent = (xmlNodePtr)sa_get_parent_group(group);
675  	else
676  		parent = (xmlNodePtr)group;
677  	zfs = xmlGetProp(parent, (xmlChar *)"zfs");
678  	if (zfs != NULL) {
679  		xmlFree(zfs);
680  		ret = 1;
681  	}
682  	return (ret);
683  }
684  
685  /*
686   * sa_get_object_type(object)
687   *
688   * This function returns a numeric value representing the object
689   * type. This allows using simpler checks when doing type specific
690   * operations.
691   */
692  
693  static int
694  sa_get_object_type(void *object)
695  {
696  	xmlNodePtr node = (xmlNodePtr)object;
697  	int type;
698  
699  	if (xmlStrcmp(node->name, (xmlChar *)"group") == 0)
700  		type = SA_TYPE_GROUP;
701  	else if (xmlStrcmp(node->name, (xmlChar *)"share") == 0)
702  		type = SA_TYPE_SHARE;
703  	else if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0)
704  		type = SA_TYPE_RESOURCE;
705  	else if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0)
706  		type = SA_TYPE_OPTIONSET;
707  	else if (xmlStrcmp(node->name, (xmlChar *)"security") == 0)
708  		type = SA_TYPE_ALTSPACE;
709  	else
710  		assert(0);
711  	return (type);
712  }
713  
714  /*
715   * sa_optionset_name(optionset, oname, len, id)
716   *	return the SMF name for the optionset. If id is not NULL, it
717   *	will have the GUID value for a share and should be used
718   *	instead of the keyword "optionset" which is used for
719   *	groups. If the optionset doesn't have a protocol type
720   *	associated with it, "default" is used. This shouldn't happen
721   *	at this point but may be desirable in the future if there are
722   *	protocol independent properties added. The name is returned in
723   *	oname.
724   */
725  
726  static int
727  sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id)
728  {
729  	char *proto;
730  	void *parent;
731  	int ptype;
732  
733  	if (id == NULL)
734  		id = "optionset";
735  
736  	parent = sa_get_optionset_parent(optionset);
737  	if (parent != NULL) {
738  		ptype = sa_get_object_type(parent);
739  		proto = sa_get_optionset_attr(optionset, "type");
740  		if (ptype != SA_TYPE_RESOURCE) {
741  			len = snprintf(oname, len, "%s_%s", id,
742  			    proto ? proto : "default");
743  		} else {
744  			char *index;
745  			index = get_node_attr((void *)parent, "id");
746  			if (index != NULL) {
747  				len = snprintf(oname, len, "%s_%s_%s", id,
748  				    proto ? proto : "default", index);
749  				sa_free_attr_string(index);
750  			} else {
751  				len = 0;
752  			}
753  		}
754  
755  		if (proto != NULL)
756  			sa_free_attr_string(proto);
757  	} else {
758  		len = 0;
759  	}
760  	return (len);
761  }
762  
763  /*
764   * sa_security_name(optionset, oname, len, id)
765   *
766   * return the SMF name for the security. If id is not NULL, it will
767   * have the GUID value for a share and should be used instead of the
768   * keyword "optionset" which is used for groups. If the optionset
769   * doesn't have a protocol type associated with it, "default" is
770   * used. This shouldn't happen at this point but may be desirable in
771   * the future if there are protocol independent properties added. The
772   * name is returned in oname. The security type is also encoded into
773   * the name. In the future, this wil *be handled a bit differently.
774   */
775  
776  static int
777  sa_security_name(sa_security_t security, char *oname, size_t len, char *id)
778  {
779  	char *proto;
780  	char *sectype;
781  
782  	if (id == NULL)
783  		id = "optionset";
784  
785  	proto = sa_get_security_attr(security, "type");
786  	sectype = sa_get_security_attr(security, "sectype");
787  	len = snprintf(oname, len, "%s_%s_%s", id, proto ? proto : "default",
788  	    sectype ? sectype : "default");
789  	if (proto != NULL)
790  		sa_free_attr_string(proto);
791  	if (sectype != NULL)
792  		sa_free_attr_string(sectype);
793  	return (len);
794  }
795  
796  /*
797   * verifydefgroupopts(handle)
798   *
799   * Make sure a "default" group exists and has default protocols enabled.
800   */
801  static void
802  verifydefgroupopts(sa_handle_t handle)
803  {
804  	sa_group_t defgrp;
805  	sa_optionset_t opt;
806  
807  	defgrp = sa_get_group(handle, "default");
808  	if (defgrp != NULL) {
809  		opt = sa_get_optionset(defgrp, NULL);
810  		/*
811  		 * NFS is the default for default group
812  		 */
813  		if (opt == NULL)
814  			opt = sa_create_optionset(defgrp, "nfs");
815  	}
816  }
817  
818  /*
819   * sa_init_impl(init_service, arg)
820   *	Initialize the API
821   *	find all the shared objects
822   *	init the tables with all objects
823   *	read in the current configuration
824   *
825   *	arg is a parameter passed in whose meaning is based on the init_service.
826   *	See libshare.h under API initialization.
827   */
828  #define	GETPROP(prop)	scf_simple_prop_next_astring(prop)
829  #define	CHECKTSTAMP(st, tval)	stat(SA_LEGACY_DFSTAB, &st) >= 0 && \
830  	tval != TSTAMP(st.st_ctim)
831  static sa_handle_t
832  sa_init_impl(int init_service, void *arg)
833  {
834  	struct stat st;
835  	/* legacy is used for debugging only as far as I can tell */
836  	int legacy = 0;
837  	uint64_t tval = 0;
838  	int lockfd;
839  	sigset_t old;
840  	int updatelegacy = B_FALSE;
841  	scf_simple_prop_t *prop;
842  	sa_handle_impl_t handle;
843  	int err;
844  
845  	handle = calloc(sizeof (struct sa_handle_impl), 1);
846  
847  	if (handle != NULL) {
848  		handle->sa_service = init_service;
849  		/*
850  		 * Get protocol specific structures, but only if this
851  		 * is the only handle.
852  		 */
853  		(void) mutex_lock(&sa_global_lock);
854  		if (sa_global_handles == NULL)
855  			(void) proto_plugin_init();
856  		(void) mutex_unlock(&sa_global_lock);
857  		if (init_service & (SA_INIT_SHARE_API |
858  		    SA_INIT_SHARE_API_SELECTIVE | SA_INIT_ONE_SHARE_FROM_NAME |
859  		    SA_INIT_ONE_SHARE_FROM_HANDLE)) {
860  			/*
861  			 * initialize access into libzfs. We use this
862  			 * when collecting info about ZFS datasets and
863  			 * shares.
864  			 */
865  			if (sa_zfs_init(handle) == B_FALSE) {
866  				free(handle);
867  				(void) mutex_lock(&sa_global_lock);
868  				(void) proto_plugin_fini();
869  				(void) mutex_unlock(&sa_global_lock);
870  				return (NULL);
871  			}
872  			/*
873  			 * since we want to use SMF, initialize an svc handle
874  			 * and find out what is there.
875  			 */
876  			handle->scfhandle = sa_scf_init(handle);
877  			if (handle->scfhandle != NULL) {
878  				/*
879  				 * Need to lock the extraction of the
880  				 * configuration if the dfstab file has
881  				 * changed. Lock everything now and release if
882  				 * not needed.  Use a file that isn't being
883  				 * manipulated by other parts of the system in
884  				 * order to not interfere with locking. Using
885  				 * dfstab doesn't work.
886  				 */
887  				sablocksigs(&old);
888  				lockfd = open(DFS_LOCK_FILE, O_RDWR);
889  				if (lockfd >= 0) {
890  					errno = 0;
891  					(void) lockf(lockfd, F_LOCK, 0);
892  					(void) mutex_lock(&sa_dfstab_lock);
893  					/*
894  					 * Check whether we are going to need
895  					 * to merge any dfstab changes. This
896  					 * is done by comparing the value of
897  					 * legacy-timestamp with the current
898  					 * st_ctim of the file. If they are
899  					 * different, an update is needed and
900  					 * the file must remain locked until
901  					 * the merge is done in order to
902  					 * prevent multiple startups from
903  					 * changing the SMF repository at the
904  					 * same time.  The first to get the
905  					 * lock will make any changes before
906  					 * the others can read the repository.
907  					 */
908  					prop = scf_simple_prop_get
909  					    (handle->scfhandle->handle,
910  					    (const char *)SA_SVC_FMRI_BASE
911  					    ":default", "operation",
912  					    "legacy-timestamp");
913  					if (prop != NULL) {
914  						char *i64;
915  						i64 = GETPROP(prop);
916  						if (i64 != NULL)
917  							tval = strtoull(i64,
918  							    NULL, 0);
919  						if (CHECKTSTAMP(st, tval))
920  							updatelegacy = B_TRUE;
921  						scf_simple_prop_free(prop);
922  					} else {
923  						/*
924  						 * We haven't set the
925  						 * timestamp before so do it.
926  						 */
927  						updatelegacy = B_TRUE;
928  					}
929  					if (updatelegacy == B_FALSE) {
930  						(void) mutex_unlock(
931  						    &sa_dfstab_lock);
932  						(void) lockf(lockfd, F_ULOCK,
933  						    0);
934  						(void) close(lockfd);
935  					}
936  
937  				}
938  				/*
939  				 * It is essential that the document tree and
940  				 * the internal list of roots to handles be
941  				 * setup before anything that might try to
942  				 * create a new object is called. The document
943  				 * tree is the combination of handle->doc and
944  				 * handle->tree. This allows searches,
945  				 * etc. when all you have is an object in the
946  				 * tree.
947  				 */
948  				handle->doc = xmlNewDoc((xmlChar *)"1.0");
949  				handle->tree = xmlNewNode(NULL,
950  				    (xmlChar *)"sharecfg");
951  				if (handle->doc != NULL &&
952  				    handle->tree != NULL) {
953  					(void) xmlDocSetRootElement(handle->doc,
954  					    handle->tree);
955  					err = add_handle_for_root(handle->tree,
956  					    handle);
957  					if (err == SA_OK)
958  						err = sa_get_config(
959  						    handle->scfhandle,
960  						    handle->tree, handle);
961  				} else {
962  					if (handle->doc != NULL)
963  						xmlFreeDoc(handle->doc);
964  					if (handle->tree != NULL)
965  						xmlFreeNode(handle->tree);
966  					err = SA_NO_MEMORY;
967  				}
968  
969  				saunblocksigs(&old);
970  
971  				if (err != SA_OK) {
972  					/*
973  					 * If we couldn't add the tree handle
974  					 * to the list, then things are going
975  					 * to fail badly. Might as well undo
976  					 * everything now and fail the
977  					 * sa_init().
978  					 */
979  					sa_fini(handle);
980  					if (updatelegacy == B_TRUE) {
981  						(void) mutex_unlock(
982  						    &sa_dfstab_lock);
983  						(void) lockf(lockfd,
984  						    F_ULOCK, 0);
985  						(void) close(lockfd);
986  					}
987  					return (NULL);
988  				}
989  
990  				if (tval == 0) {
991  					/*
992  					 * first time so make sure
993  					 * default is setup
994  					 */
995  					verifydefgroupopts(handle);
996  				}
997  
998  				if (updatelegacy == B_TRUE) {
999  					sablocksigs(&old);
1000  					getlegacyconfig((sa_handle_t)handle,
1001  					    SA_LEGACY_DFSTAB, &handle->tree);
1002  					if (stat(SA_LEGACY_DFSTAB, &st) >= 0)
1003  						set_legacy_timestamp(
1004  						    handle->tree,
1005  						    SA_LEGACY_DFSTAB,
1006  						    TSTAMP(st.st_ctim));
1007  					saunblocksigs(&old);
1008  					/*
1009  					 * Safe to unlock now to allow
1010  					 * others to run
1011  					 */
1012  					(void) mutex_unlock(&sa_dfstab_lock);
1013  					(void) lockf(lockfd, F_ULOCK, 0);
1014  					(void) close(lockfd);
1015  				}
1016  				/* Get sharetab timestamp */
1017  				sa_update_sharetab_ts((sa_handle_t)handle);
1018  
1019  				/* Get lastupdate (transaction) timestamp */
1020  				prop = scf_simple_prop_get(
1021  				    handle->scfhandle->handle,
1022  				    (const char *)SA_SVC_FMRI_BASE ":default",
1023  				    "state", "lastupdate");
1024  				if (prop != NULL) {
1025  					char *str;
1026  					str =
1027  					    scf_simple_prop_next_astring(prop);
1028  					if (str != NULL)
1029  						handle->tstrans =
1030  						    strtoull(str, NULL, 0);
1031  					else
1032  						handle->tstrans = 0;
1033  					scf_simple_prop_free(prop);
1034  				}
1035  				/*
1036  				 * In this conditional the library reads from
1037  				 * zfs and /etc/dfs/sharetab to find datasets
1038  				 * that must be shared. The result is a tree of
1039  				 * groups that are stored in the handle for
1040  				 * libshare to utilize later when asked to share
1041  				 * or unshare datasets.
1042  				 */
1043  				if (init_service &
1044  				    SA_INIT_SHARE_API_SELECTIVE) {
1045  					char **paths;
1046  					size_t paths_len, i;
1047  
1048  					legacy |= sa_get_one_zfs_share(handle,
1049  					    "zfs",
1050  					    (sa_init_selective_arg_t *)arg,
1051  					    &paths, &paths_len);
1052  					legacy |= get_one_transient(handle,
1053  					    &handle->tree, paths, paths_len);
1054  					for (i = 0; i < paths_len; ++i) {
1055  						free(paths[i]);
1056  					}
1057  					free(paths);
1058  				} else if (init_service &
1059  				    SA_INIT_ONE_SHARE_FROM_NAME) {
1060  					char path[ZFS_MAXPROPLEN];
1061  					char *ptr = path;
1062  					char **ptr_to_path = &ptr;
1063  
1064  					legacy |=
1065  					    sa_get_zfs_share_for_name(handle,
1066  					    "zfs", (char *)arg, path);
1067  					legacy |= get_one_transient(handle,
1068  					    &handle->tree, ptr_to_path, 1);
1069  				} else if (init_service &
1070  				    SA_INIT_ONE_SHARE_FROM_HANDLE) {
1071  					char path[ZFS_MAXPROPLEN];
1072  					char *ptr = path;
1073  					char **ptr_to_path = &ptr;
1074  
1075  					legacy |=
1076  					    sa_get_zfs_share_for_name(handle,
1077  					    "zfs",
1078  					    zfs_get_name(
1079  					    (zfs_handle_t *)arg),
1080  					    path);
1081  					legacy |= get_one_transient(handle,
1082  					    &handle->tree, ptr_to_path, 1);
1083  				} else {
1084  					legacy |= sa_get_zfs_shares(handle,
1085  					    "zfs");
1086  					legacy |= gettransients(handle,
1087  					    &handle->tree);
1088  				}
1089  			}
1090  		}
1091  	}
1092  	return ((sa_handle_t)handle);
1093  }
1094  
1095  /*
1096   * sa_init exists as a legacy interface, new consumers should use sa_init_arg.
1097   */
1098  sa_handle_t
1099  sa_init(int init_service)
1100  {
1101  	return (sa_init_impl(init_service, NULL));
1102  }
1103  
1104  /*
1105   * See libshare.h "API Initialization" section for valid values of init_service
1106   * as well as the appropriate argument type for a given init_service.
1107   */
1108  sa_handle_t
1109  sa_init_arg(int init_service, void *arg)
1110  {
1111  	return (sa_init_impl(init_service, arg));
1112  }
1113  
1114  /*
1115   * sa_fini(handle)
1116   *	Uninitialize the API structures including the configuration
1117   *	data structures and ZFS related data.
1118   */
1119  
1120  void
1121  sa_fini(sa_handle_t handle)
1122  {
1123  	sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
1124  
1125  	if (impl_handle != NULL) {
1126  		/*
1127  		 * Free the config trees and any other data structures
1128  		 * used in the handle.
1129  		 */
1130  		if (impl_handle->doc != NULL)
1131  			xmlFreeDoc(impl_handle->doc);
1132  
1133  		/* Remove and free the entry in the global list. */
1134  		remove_handle_for_root(impl_handle->tree);
1135  
1136  		/*
1137  		 * If this was the last handle to release, unload the
1138  		 * plugins that were loaded. Use a mutex in case
1139  		 * another thread is reinitializing.
1140  		 */
1141  		(void) mutex_lock(&sa_global_lock);
1142  		if (sa_global_handles == NULL)
1143  			(void) proto_plugin_fini();
1144  		(void) mutex_unlock(&sa_global_lock);
1145  
1146  		sa_scf_fini(impl_handle->scfhandle);
1147  		sa_zfs_fini(impl_handle);
1148  
1149  		/* Make sure we free the handle */
1150  		free(impl_handle);
1151  
1152  	}
1153  }
1154  
1155  /*
1156   * sa_get_protocols(char **protocol)
1157   *	Get array of protocols that are supported
1158   *	Returns pointer to an allocated and NULL terminated
1159   *	array of strings.  Caller must free.
1160   *	This really should be determined dynamically.
1161   *	If there aren't any defined, return -1.
1162   *	Use free() to return memory.
1163   */
1164  
1165  int
1166  sa_get_protocols(char ***protocols)
1167  {
1168  	int numproto = -1;
1169  
1170  	if (protocols != NULL) {
1171  		struct sa_proto_plugin *plug;
1172  		for (numproto = 0, plug = sap_proto_list; plug != NULL;
1173  		    plug = plug->plugin_next) {
1174  			numproto++;
1175  		}
1176  
1177  		*protocols = calloc(numproto + 1,  sizeof (char *));
1178  		if (*protocols != NULL) {
1179  			int ret = 0;
1180  			for (plug = sap_proto_list; plug != NULL;
1181  			    plug = plug->plugin_next) {
1182  				/* faking for now */
1183  				(*protocols)[ret++] =
1184  				    plug->plugin_ops->sa_protocol;
1185  			}
1186  		} else {
1187  			numproto = -1;
1188  		}
1189  	}
1190  	return (numproto);
1191  }
1192  
1193  /*
1194   * find_group_by_name(node, group)
1195   *
1196   * search the XML document subtree specified by node to find the group
1197   * specified by group. Searching subtree allows subgroups to be
1198   * searched for.
1199   */
1200  
1201  static xmlNodePtr
1202  find_group_by_name(xmlNodePtr node, xmlChar *group)
1203  {
1204  	xmlChar *name = NULL;
1205  
1206  	for (node = node->xmlChildrenNode; node != NULL;
1207  	    node = node->next) {
1208  		if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) {
1209  			/* if no groupname, return the first found */
1210  			if (group == NULL)
1211  				break;
1212  			name = xmlGetProp(node, (xmlChar *)"name");
1213  			if (name != NULL && xmlStrcmp(name, group) == 0)
1214  				break;
1215  			if (name != NULL) {
1216  				xmlFree(name);
1217  				name = NULL;
1218  			}
1219  		}
1220  	}
1221  	if (name != NULL)
1222  		xmlFree(name);
1223  	return (node);
1224  }
1225  
1226  /*
1227   * sa_get_group(groupname)
1228   *	Return the "group" specified.  If groupname is NULL,
1229   *	return the first group of the list of groups.
1230   */
1231  sa_group_t
1232  sa_get_group(sa_handle_t handle, char *groupname)
1233  {
1234  	xmlNodePtr node = NULL;
1235  	char *subgroup = NULL;
1236  	char *group = NULL;
1237  	sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
1238  
1239  	if (impl_handle != NULL && impl_handle->tree != NULL) {
1240  		if (groupname != NULL) {
1241  			group = strdup(groupname);
1242  			if (group != NULL) {
1243  				subgroup = strchr(group, '/');
1244  				if (subgroup != NULL)
1245  					*subgroup++ = '\0';
1246  			}
1247  		}
1248  		/*
1249  		 * We want to find the, possibly, named group. If
1250  		 * group is not NULL, then lookup the name. If it is
1251  		 * NULL, we only do the find if groupname is also
1252  		 * NULL. This allows lookup of the "first" group in
1253  		 * the internal list.
1254  		 */
1255  		if (group != NULL || groupname == NULL)
1256  			node = find_group_by_name(impl_handle->tree,
1257  			    (xmlChar *)group);
1258  
1259  		/* if a subgroup, find it before returning */
1260  		if (subgroup != NULL && node != NULL)
1261  			node = find_group_by_name(node, (xmlChar *)subgroup);
1262  	}
1263  	if (node != NULL && (char *)group != NULL)
1264  		(void) sa_get_instance(impl_handle->scfhandle, (char *)group);
1265  	if (group != NULL)
1266  		free(group);
1267  	return ((sa_group_t)(node));
1268  }
1269  
1270  /*
1271   * sa_get_next_group(group)
1272   *	Return the "next" group after the specified group from
1273   *	the internal group list.  NULL if there are no more.
1274   */
1275  sa_group_t
1276  sa_get_next_group(sa_group_t group)
1277  {
1278  	xmlNodePtr ngroup = NULL;
1279  	if (group != NULL) {
1280  		for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL;
1281  		    ngroup = ngroup->next) {
1282  			if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0)
1283  				break;
1284  		}
1285  	}
1286  	return ((sa_group_t)ngroup);
1287  }
1288  
1289  /*
1290   * sa_get_share(group, sharepath)
1291   *	Return the share object for the share specified. The share
1292   *	must be in the specified group.  Return NULL if not found.
1293   */
1294  sa_share_t
1295  sa_get_share(sa_group_t group, char *sharepath)
1296  {
1297  	xmlNodePtr node = NULL;
1298  	xmlChar *path;
1299  
1300  	/*
1301  	 * For future scalability, this should end up building a cache
1302  	 * since it will get called regularly by the mountd and info
1303  	 * services.
1304  	 */
1305  	if (group != NULL) {
1306  		for (node = ((xmlNodePtr)group)->children; node != NULL;
1307  		    node = node->next) {
1308  			if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1309  				if (sharepath == NULL) {
1310  					break;
1311  				} else {
1312  					/* is it the correct share? */
1313  					path = xmlGetProp(node,
1314  					    (xmlChar *)"path");
1315  					if (path != NULL &&
1316  					    xmlStrcmp(path,
1317  					    (xmlChar *)sharepath) == 0) {
1318  						xmlFree(path);
1319  						break;
1320  					}
1321  					xmlFree(path);
1322  				}
1323  			}
1324  		}
1325  	}
1326  	return ((sa_share_t)node);
1327  }
1328  
1329  /*
1330   * sa_get_next_share(share)
1331   *	Return the next share following the specified share
1332   *	from the internal list of shares. Returns NULL if there
1333   *	are no more shares.  The list is relative to the same
1334   *	group.
1335   */
1336  sa_share_t
1337  sa_get_next_share(sa_share_t share)
1338  {
1339  	xmlNodePtr node = NULL;
1340  
1341  	if (share != NULL) {
1342  		for (node = ((xmlNodePtr)share)->next; node != NULL;
1343  		    node = node->next) {
1344  			if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1345  				break;
1346  			}
1347  		}
1348  	}
1349  	return ((sa_share_t)node);
1350  }
1351  
1352  /*
1353   * _sa_get_child_node(node, type)
1354   *
1355   * find the child node of the specified node that has "type". This is
1356   * used to implement several internal functions.
1357   */
1358  
1359  static xmlNodePtr
1360  _sa_get_child_node(xmlNodePtr node, xmlChar *type)
1361  {
1362  	xmlNodePtr child;
1363  	for (child = node->xmlChildrenNode; child != NULL;
1364  	    child = child->next)
1365  		if (xmlStrcmp(child->name, type) == 0)
1366  			return (child);
1367  	return ((xmlNodePtr)NULL);
1368  }
1369  
1370  /*
1371   *  find_share(group, path)
1372   *
1373   * Search all the shares in the specified group for one that has the
1374   * specified path.
1375   */
1376  
1377  static sa_share_t
1378  find_share(sa_group_t group, char *sharepath)
1379  {
1380  	sa_share_t share;
1381  	char *path;
1382  
1383  	for (share = sa_get_share(group, NULL); share != NULL;
1384  	    share = sa_get_next_share(share)) {
1385  		path = sa_get_share_attr(share, "path");
1386  		if (path != NULL && strcmp(path, sharepath) == 0) {
1387  			sa_free_attr_string(path);
1388  			break;
1389  		}
1390  		if (path != NULL)
1391  			sa_free_attr_string(path);
1392  	}
1393  	return (share);
1394  }
1395  
1396  /*
1397   * sa_get_sub_group(group)
1398   *
1399   * Get the first sub-group of group. The sa_get_next_group() function
1400   * can be used to get the rest. This is currently only used for ZFS
1401   * sub-groups but could be used to implement a more general mechanism.
1402   */
1403  
1404  sa_group_t
1405  sa_get_sub_group(sa_group_t group)
1406  {
1407  	return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1408  	    (xmlChar *)"group"));
1409  }
1410  
1411  /*
1412   * sa_find_share(sharepath)
1413   *	Finds a share regardless of group.  In the future, this
1414   *	function should utilize a cache and hash table of some kind.
1415   *	The current assumption is that a path will only be shared
1416   *	once.  In the future, this may change as implementation of
1417   *	resource names comes into being.
1418   */
1419  sa_share_t
1420  sa_find_share(sa_handle_t handle, char *sharepath)
1421  {
1422  	sa_group_t group;
1423  	sa_group_t zgroup;
1424  	sa_share_t share = NULL;
1425  	int done = 0;
1426  
1427  	for (group = sa_get_group(handle, NULL); group != NULL && !done;
1428  	    group = sa_get_next_group(group)) {
1429  		if (is_zfs_group(group)) {
1430  			for (zgroup =
1431  			    (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1432  			    (xmlChar *)"group");
1433  			    zgroup != NULL;
1434  			    zgroup = sa_get_next_group(zgroup)) {
1435  				share = find_share(zgroup, sharepath);
1436  				if (share != NULL)
1437  					break;
1438  			}
1439  		} else {
1440  			share = find_share(group, sharepath);
1441  		}
1442  		if (share != NULL)
1443  			break;
1444  	}
1445  	return (share);
1446  }
1447  
1448  /*
1449   *  sa_check_path(group, path, strictness)
1450   *
1451   * Check that path is a valid path relative to the group.  Currently,
1452   * we are ignoring the group and checking only the NFS rules. Later,
1453   * we may want to use the group to then check against the protocols
1454   * enabled on the group. The strictness values mean:
1455   * SA_CHECK_NORMAL == only check newpath against shares that are active
1456   * SA_CHECK_STRICT == check newpath against both active shares and those
1457   *		      stored in the repository
1458   */
1459  
1460  int
1461  sa_check_path(sa_group_t group, char *path, int strictness)
1462  {
1463  	sa_handle_t handle;
1464  
1465  	handle = sa_find_group_handle(group);
1466  	if (handle == NULL)
1467  		return (SA_BAD_PATH);
1468  
1469  	return (validpath(handle, path, strictness));
1470  }
1471  
1472  /*
1473   * mark_excluded_protos(group, share, flags)
1474   *
1475   * Walk through all the protocols enabled for the group and check to
1476   * see if the share has any of them should be in the exclude list
1477   * based on the featureset of the protocol. If there are any, add the
1478   * "exclude" property to the share.
1479   */
1480  static void
1481  mark_excluded_protos(sa_group_t group, xmlNodePtr share, uint64_t flags)
1482  {
1483  	sa_optionset_t optionset;
1484  	char exclude_list[SA_STRSIZE];
1485  	char *sep = "";
1486  
1487  	exclude_list[0] = '\0';
1488  	for (optionset = sa_get_optionset(group, NULL);
1489  	    optionset != NULL;
1490  	    optionset = sa_get_next_optionset(optionset)) {
1491  		char *value;
1492  		uint64_t features;
1493  		value = sa_get_optionset_attr(optionset, "type");
1494  		if (value == NULL)
1495  			continue;
1496  		features = sa_proto_get_featureset(value);
1497  		if (!(features & flags)) {
1498  			(void) strlcat(exclude_list, sep,
1499  			    sizeof (exclude_list));
1500  			(void) strlcat(exclude_list, value,
1501  			    sizeof (exclude_list));
1502  			sep = ",";
1503  		}
1504  		sa_free_attr_string(value);
1505  	}
1506  	if (exclude_list[0] != '\0')
1507  		(void) xmlSetProp(share, (xmlChar *)"exclude",
1508  		    (xmlChar *)exclude_list);
1509  }
1510  
1511  /*
1512   * get_all_features(group)
1513   *
1514   * Walk through all the protocols on the group and collect all
1515   * possible enabled features. This is the OR of all the featuresets.
1516   */
1517  static uint64_t
1518  get_all_features(sa_group_t group)
1519  {
1520  	sa_optionset_t optionset;
1521  	uint64_t features = 0;
1522  
1523  	for (optionset = sa_get_optionset(group, NULL);
1524  	    optionset != NULL;
1525  	    optionset = sa_get_next_optionset(optionset)) {
1526  		char *value;
1527  		value = sa_get_optionset_attr(optionset, "type");
1528  		if (value == NULL)
1529  			continue;
1530  		features |= sa_proto_get_featureset(value);
1531  		sa_free_attr_string(value);
1532  	}
1533  	return (features);
1534  }
1535  
1536  
1537  /*
1538   * _sa_add_share(group, sharepath, persist, *error, flags)
1539   *
1540   * Common code for all types of add_share. sa_add_share() is the
1541   * public API, we also need to be able to do this when parsing legacy
1542   * files and construction of the internal configuration while
1543   * extracting config info from SMF. "flags" indicates if some
1544   * protocols need relaxed rules while other don't. These values are
1545   * the featureset values defined in libshare.h.
1546   */
1547  
1548  sa_share_t
1549  _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error,
1550      uint64_t flags)
1551  {
1552  	xmlNodePtr node = NULL;
1553  	int err;
1554  
1555  	err  = SA_OK; /* assume success */
1556  
1557  	node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"share", NULL);
1558  	if (node == NULL) {
1559  		if (error != NULL)
1560  			*error = SA_NO_MEMORY;
1561  		return (node);
1562  	}
1563  
1564  	(void) xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath);
1565  	(void) xmlSetProp(node, (xmlChar *)"type",
1566  	    persist ? (xmlChar *)"persist" : (xmlChar *)"transient");
1567  	if (flags != 0)
1568  		mark_excluded_protos(group, node, flags);
1569  	if (persist != SA_SHARE_TRANSIENT) {
1570  		/*
1571  		 * persistent shares come in two flavors: SMF and
1572  		 * ZFS. Sort this one out based on target group and
1573  		 * path type. Both NFS and SMB are supported. First,
1574  		 * check to see if the protocol is enabled on the
1575  		 * subgroup and then setup the share appropriately.
1576  		 */
1577  		if (sa_group_is_zfs(group) &&
1578  		    sa_path_is_zfs(sharepath)) {
1579  			if (sa_get_optionset(group, "nfs") != NULL)
1580  				err = sa_zfs_set_sharenfs(group, sharepath, 1);
1581  			else if (sa_get_optionset(group, "smb") != NULL)
1582  				err = sa_zfs_set_sharesmb(group, sharepath, 1);
1583  		} else {
1584  			sa_handle_impl_t impl_handle;
1585  			impl_handle =
1586  			    (sa_handle_impl_t)sa_find_group_handle(group);
1587  			if (impl_handle != NULL) {
1588  				err = sa_commit_share(impl_handle->scfhandle,
1589  				    group, (sa_share_t)node);
1590  			} else {
1591  				err = SA_SYSTEM_ERR;
1592  			}
1593  		}
1594  	}
1595  	if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER)
1596  		/* called by the dfstab parser so could be a show */
1597  		err = SA_OK;
1598  
1599  	if (err != SA_OK) {
1600  		/*
1601  		 * we couldn't commit to the repository so undo
1602  		 * our internal state to reflect reality.
1603  		 */
1604  		xmlUnlinkNode(node);
1605  		xmlFreeNode(node);
1606  		node = NULL;
1607  	}
1608  
1609  	if (error != NULL)
1610  		*error = err;
1611  
1612  	return (node);
1613  }
1614  
1615  /*
1616   * sa_add_share(group, sharepath, persist, *error)
1617   *
1618   *	Add a new share object to the specified group.  The share will
1619   *	have the specified sharepath and will only be constructed if
1620   *	it is a valid path to be shared.  NULL is returned on error
1621   *	and a detailed error value will be returned via the error
1622   *	pointer.
1623   */
1624  sa_share_t
1625  sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
1626  {
1627  	xmlNodePtr node = NULL;
1628  	int strictness = SA_CHECK_NORMAL;
1629  	sa_handle_t handle;
1630  	uint64_t special = 0;
1631  	uint64_t features;
1632  
1633  	/*
1634  	 * If the share is to be permanent, use strict checking so a
1635  	 * bad config doesn't get created. Transient shares only need
1636  	 * to check against the currently active
1637  	 * shares. SA_SHARE_PARSER is a modifier used internally to
1638  	 * indicate that we are being called by the dfstab parser and
1639  	 * that we need strict checking in all cases. Normally persist
1640  	 * is in integer value but SA_SHARE_PARSER may be or'd into
1641  	 * it as an override.
1642  	 */
1643  	if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT)
1644  		strictness = SA_CHECK_STRICT;
1645  
1646  	handle = sa_find_group_handle(group);
1647  
1648  	/*
1649  	 * need to determine if the share is valid. The rules are:
1650  	 *	- The path must not already exist
1651  	 *	- The path must not be a subdir or parent dir of an
1652  	 *	  existing path unless at least one protocol allows it.
1653  	 * The sub/parent check is done in sa_check_path().
1654  	 */
1655  
1656  	if (sa_find_share(handle, sharepath) == NULL) {
1657  		*error = sa_check_path(group, sharepath, strictness);
1658  		features = get_all_features(group);
1659  		switch (*error) {
1660  		case SA_PATH_IS_SUBDIR:
1661  			if (features & SA_FEATURE_ALLOWSUBDIRS)
1662  				special |= SA_FEATURE_ALLOWSUBDIRS;
1663  			break;
1664  		case SA_PATH_IS_PARENTDIR:
1665  			if (features & SA_FEATURE_ALLOWPARDIRS)
1666  				special |= SA_FEATURE_ALLOWPARDIRS;
1667  			break;
1668  		}
1669  		if (*error == SA_OK || special != SA_FEATURE_NONE)
1670  			node = _sa_add_share(group, sharepath, persist,
1671  			    error, special);
1672  	} else {
1673  		*error = SA_DUPLICATE_NAME;
1674  	}
1675  
1676  	return ((sa_share_t)node);
1677  }
1678  
1679  /*
1680   * sa_enable_share(share, protocol)
1681   *	Enable the specified share to the specified protocol.
1682   *	If protocol is NULL, then all protocols.
1683   */
1684  int
1685  sa_enable_share(sa_share_t share, char *protocol)
1686  {
1687  	char *sharepath;
1688  	struct stat st;
1689  	int err = SA_OK;
1690  	int ret;
1691  
1692  	sharepath = sa_get_share_attr(share, "path");
1693  	if (sharepath == NULL)
1694  		return (SA_NO_MEMORY);
1695  	if (stat(sharepath, &st) < 0) {
1696  		err = SA_NO_SUCH_PATH;
1697  	} else {
1698  		/* tell the server about the share */
1699  		if (protocol != NULL) {
1700  			if (excluded_protocol(share, protocol))
1701  				goto done;
1702  
1703  			/* lookup protocol specific handler */
1704  			err = sa_proto_share(protocol, share);
1705  			if (err == SA_OK)
1706  				(void) sa_set_share_attr(share,
1707  				    "shared", "true");
1708  		} else {
1709  			/* Tell all protocols about the share */
1710  			sa_group_t group;
1711  			sa_optionset_t optionset;
1712  
1713  			group = sa_get_parent_group(share);
1714  
1715  			for (optionset = sa_get_optionset(group, NULL);
1716  			    optionset != NULL;
1717  			    optionset = sa_get_next_optionset(optionset)) {
1718  				char *proto;
1719  				proto = sa_get_optionset_attr(optionset,
1720  				    "type");
1721  				if (proto != NULL) {
1722  					if (!excluded_protocol(share, proto)) {
1723  						ret = sa_proto_share(proto,
1724  						    share);
1725  						if (ret != SA_OK)
1726  							err = ret;
1727  					}
1728  					sa_free_attr_string(proto);
1729  				}
1730  			}
1731  			(void) sa_set_share_attr(share, "shared", "true");
1732  		}
1733  	}
1734  done:
1735  	if (sharepath != NULL)
1736  		sa_free_attr_string(sharepath);
1737  	return (err);
1738  }
1739  
1740  /*
1741   * sa_disable_share(share, protocol)
1742   *	Disable the specified share to the specified protocol.  If
1743   *	protocol is NULL, then all protocols that are enabled for the
1744   *	share should be disabled.
1745   */
1746  int
1747  sa_disable_share(sa_share_t share, char *protocol)
1748  {
1749  	char *path;
1750  	int err = SA_OK;
1751  	int ret = SA_OK;
1752  
1753  	path = sa_get_share_attr(share, "path");
1754  
1755  	if (protocol != NULL) {
1756  		ret = sa_proto_unshare(share, protocol, path);
1757  	} else {
1758  		/* need to do all protocols */
1759  		sa_group_t group;
1760  		sa_optionset_t optionset;
1761  
1762  		group = sa_get_parent_group(share);
1763  
1764  		/* Tell all protocols about the share */
1765  		for (optionset = sa_get_optionset(group, NULL);
1766  		    optionset != NULL;
1767  		    optionset = sa_get_next_optionset(optionset)) {
1768  			char *proto;
1769  
1770  			proto = sa_get_optionset_attr(optionset, "type");
1771  			if (proto != NULL) {
1772  				err = sa_proto_unshare(share, proto, path);
1773  				if (err != SA_OK)
1774  					ret = err;
1775  				sa_free_attr_string(proto);
1776  			}
1777  		}
1778  	}
1779  	if (ret == SA_OK)
1780  		(void) sa_set_share_attr(share, "shared", NULL);
1781  	if (path != NULL)
1782  		sa_free_attr_string(path);
1783  	return (ret);
1784  }
1785  
1786  /*
1787   * sa_remove_share(share)
1788   *
1789   * remove the specified share from its containing group.
1790   * Remove from the SMF or ZFS configuration space.
1791   */
1792  
1793  int
1794  sa_remove_share(sa_share_t share)
1795  {
1796  	sa_group_t group;
1797  	int ret = SA_OK;
1798  	char *type;
1799  	int transient = 0;
1800  	char *groupname;
1801  	char *zfs;
1802  
1803  	type = sa_get_share_attr(share, "type");
1804  	group = sa_get_parent_group(share);
1805  	zfs = sa_get_group_attr(group, "zfs");
1806  	groupname = sa_get_group_attr(group, "name");
1807  	if (type != NULL && strcmp(type, "persist") != 0)
1808  		transient = 1;
1809  	if (type != NULL)
1810  		sa_free_attr_string(type);
1811  
1812  	/* remove the node from its group then free the memory */
1813  
1814  	/*
1815  	 * need to test if "busy"
1816  	 */
1817  	/* only do SMF action if permanent */
1818  	if (!transient || zfs != NULL) {
1819  		/* remove from legacy dfstab as well as possible SMF */
1820  		ret = sa_delete_legacy(share, NULL);
1821  		if (ret == SA_OK) {
1822  			if (!sa_group_is_zfs(group)) {
1823  				sa_handle_impl_t impl_handle;
1824  				impl_handle = (sa_handle_impl_t)
1825  				    sa_find_group_handle(group);
1826  				if (impl_handle != NULL) {
1827  					ret = sa_delete_share(
1828  					    impl_handle->scfhandle, group,
1829  					    share);
1830  				} else {
1831  					ret = SA_SYSTEM_ERR;
1832  				}
1833  			} else {
1834  				char *sharepath = sa_get_share_attr(share,
1835  				    "path");
1836  				if (sharepath != NULL) {
1837  					ret = sa_zfs_set_sharenfs(group,
1838  					    sharepath, 0);
1839  					sa_free_attr_string(sharepath);
1840  				}
1841  			}
1842  		}
1843  	}
1844  	if (groupname != NULL)
1845  		sa_free_attr_string(groupname);
1846  	if (zfs != NULL)
1847  		sa_free_attr_string(zfs);
1848  
1849  	xmlUnlinkNode((xmlNodePtr)share);
1850  	xmlFreeNode((xmlNodePtr)share);
1851  	return (ret);
1852  }
1853  
1854  /*
1855   * sa_move_share(group, share)
1856   *
1857   * move the specified share to the specified group.  Update SMF
1858   * appropriately.
1859   */
1860  
1861  int
1862  sa_move_share(sa_group_t group, sa_share_t share)
1863  {
1864  	sa_group_t oldgroup;
1865  	int ret = SA_OK;
1866  
1867  	/* remove the node from its group then free the memory */
1868  
1869  	oldgroup = sa_get_parent_group(share);
1870  	if (oldgroup != group) {
1871  		sa_handle_impl_t impl_handle;
1872  		xmlUnlinkNode((xmlNodePtr)share);
1873  		/*
1874  		 * now that the share isn't in its old group, add to
1875  		 * the new one
1876  		 */
1877  		(void) xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share);
1878  		/* need to deal with SMF */
1879  		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1880  		if (impl_handle != NULL) {
1881  			/*
1882  			 * need to remove from old group first and then add to
1883  			 * new group. Ideally, we would do the other order but
1884  			 * need to avoid having the share in two groups at the
1885  			 * same time.
1886  			 */
1887  			ret = sa_delete_share(impl_handle->scfhandle, oldgroup,
1888  			    share);
1889  			if (ret == SA_OK)
1890  				ret = sa_commit_share(impl_handle->scfhandle,
1891  				    group, share);
1892  		} else {
1893  			ret = SA_SYSTEM_ERR;
1894  		}
1895  	}
1896  	return (ret);
1897  }
1898  
1899  /*
1900   * sa_get_parent_group(share)
1901   *
1902   * Return the containing group for the share. If a group was actually
1903   * passed in, we don't want a parent so return NULL.
1904   */
1905  
1906  sa_group_t
1907  sa_get_parent_group(sa_share_t share)
1908  {
1909  	xmlNodePtr node = NULL;
1910  	if (share != NULL) {
1911  		node = ((xmlNodePtr)share)->parent;
1912  		/*
1913  		 * make sure parent is a group and not sharecfg since
1914  		 * we may be cheating and passing in a group.
1915  		 * Eventually, groups of groups might come into being.
1916  		 */
1917  		if (node == NULL ||
1918  		    xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0)
1919  			node = NULL;
1920  	}
1921  	return ((sa_group_t)node);
1922  }
1923  
1924  /*
1925   * _sa_create_group(impl_handle, groupname)
1926   *
1927   * Create a group in the document. The caller will need to deal with
1928   * configuration store and activation.
1929   */
1930  
1931  sa_group_t
1932  _sa_create_group(sa_handle_impl_t impl_handle, char *groupname)
1933  {
1934  	xmlNodePtr node = NULL;
1935  
1936  	if (sa_valid_group_name(groupname)) {
1937  		node = xmlNewChild(impl_handle->tree, NULL, (xmlChar *)"group",
1938  		    NULL);
1939  		if (node != NULL) {
1940  			(void) xmlSetProp(node, (xmlChar *)"name",
1941  			    (xmlChar *)groupname);
1942  			(void) xmlSetProp(node, (xmlChar *)"state",
1943  			    (xmlChar *)"enabled");
1944  		}
1945  	}
1946  	return ((sa_group_t)node);
1947  }
1948  
1949  /*
1950   * _sa_create_zfs_group(group, groupname)
1951   *
1952   * Create a ZFS subgroup under the specified group. This may
1953   * eventually form the basis of general sub-groups, but is currently
1954   * restricted to ZFS.
1955   */
1956  sa_group_t
1957  _sa_create_zfs_group(sa_group_t group, char *groupname)
1958  {
1959  	xmlNodePtr node = NULL;
1960  
1961  	node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"group", NULL);
1962  	if (node != NULL) {
1963  		(void) xmlSetProp(node, (xmlChar *)"name",
1964  		    (xmlChar *)groupname);
1965  		(void) xmlSetProp(node, (xmlChar *)"state",
1966  		    (xmlChar *)"enabled");
1967  	}
1968  
1969  	return ((sa_group_t)node);
1970  }
1971  
1972  /*
1973   * sa_create_group(groupname, *error)
1974   *
1975   * Create a new group with groupname.  Need to validate that it is a
1976   * legal name for SMF and the construct the SMF service instance of
1977   * svc:/network/shares/group to implement the group. All necessary
1978   * operational properties must be added to the group at this point
1979   * (via the SMF transaction model).
1980   */
1981  sa_group_t
1982  sa_create_group(sa_handle_t handle, char *groupname, int *error)
1983  {
1984  	xmlNodePtr node = NULL;
1985  	sa_group_t group;
1986  	int ret;
1987  	char rbacstr[SA_STRSIZE];
1988  	sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
1989  
1990  	ret = SA_OK;
1991  
1992  	if (impl_handle == NULL || impl_handle->scfhandle == NULL) {
1993  		ret = SA_SYSTEM_ERR;
1994  		goto err;
1995  	}
1996  
1997  	group = sa_get_group(handle, groupname);
1998  	if (group != NULL) {
1999  		ret = SA_DUPLICATE_NAME;
2000  	} else {
2001  		if (sa_valid_group_name(groupname)) {
2002  			node = xmlNewChild(impl_handle->tree, NULL,
2003  			    (xmlChar *)"group", NULL);
2004  			if (node != NULL) {
2005  				(void) xmlSetProp(node, (xmlChar *)"name",
2006  				    (xmlChar *)groupname);
2007  				/* default to the group being enabled */
2008  				(void) xmlSetProp(node, (xmlChar *)"state",
2009  				    (xmlChar *)"enabled");
2010  				ret = sa_create_instance(impl_handle->scfhandle,
2011  				    groupname);
2012  				if (ret == SA_OK) {
2013  					ret = sa_start_transaction(
2014  					    impl_handle->scfhandle,
2015  					    "operation");
2016  				}
2017  				if (ret == SA_OK) {
2018  					ret = sa_set_property(
2019  					    impl_handle->scfhandle,
2020  					    "state", "enabled");
2021  					if (ret == SA_OK) {
2022  						ret = sa_end_transaction(
2023  						    impl_handle->scfhandle,
2024  						    impl_handle);
2025  					} else {
2026  						sa_abort_transaction(
2027  						    impl_handle->scfhandle);
2028  					}
2029  				}
2030  				if (ret == SA_OK) {
2031  					/* initialize the RBAC strings */
2032  					ret = sa_start_transaction(
2033  					    impl_handle->scfhandle,
2034  					    "general");
2035  					if (ret == SA_OK) {
2036  						(void) snprintf(rbacstr,
2037  						    sizeof (rbacstr), "%s.%s",
2038  						    SA_RBAC_MANAGE, groupname);
2039  						ret = sa_set_property(
2040  						    impl_handle->scfhandle,
2041  						    "action_authorization",
2042  						    rbacstr);
2043  					}
2044  					if (ret == SA_OK) {
2045  						(void) snprintf(rbacstr,
2046  						    sizeof (rbacstr), "%s.%s",
2047  						    SA_RBAC_VALUE, groupname);
2048  						ret = sa_set_property(
2049  						    impl_handle->scfhandle,
2050  						    "value_authorization",
2051  						    rbacstr);
2052  					}
2053  					if (ret == SA_OK) {
2054  						ret = sa_end_transaction(
2055  						    impl_handle->scfhandle,
2056  						    impl_handle);
2057  					} else {
2058  						sa_abort_transaction(
2059  						    impl_handle->scfhandle);
2060  					}
2061  				}
2062  				if (ret != SA_OK) {
2063  					/*
2064  					 * Couldn't commit the group
2065  					 * so we need to undo
2066  					 * internally.
2067  					 */
2068  					xmlUnlinkNode(node);
2069  					xmlFreeNode(node);
2070  					node = NULL;
2071  				}
2072  			} else {
2073  				ret = SA_NO_MEMORY;
2074  			}
2075  		} else {
2076  			ret = SA_INVALID_NAME;
2077  		}
2078  	}
2079  err:
2080  	if (error != NULL)
2081  		*error = ret;
2082  	return ((sa_group_t)node);
2083  }
2084  
2085  /*
2086   * sa_remove_group(group)
2087   *
2088   * Remove the specified group. This deletes from the SMF repository.
2089   * All property groups and properties are removed.
2090   */
2091  
2092  int
2093  sa_remove_group(sa_group_t group)
2094  {
2095  	char *name;
2096  	int ret = SA_OK;
2097  	sa_handle_impl_t impl_handle;
2098  
2099  	impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2100  	if (impl_handle != NULL) {
2101  		name = sa_get_group_attr(group, "name");
2102  		if (name != NULL) {
2103  			ret = sa_delete_instance(impl_handle->scfhandle, name);
2104  			sa_free_attr_string(name);
2105  		}
2106  		xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */
2107  		xmlFreeNode((xmlNodePtr)group);   /* now it is gone */
2108  	} else {
2109  		ret = SA_SYSTEM_ERR;
2110  	}
2111  	return (ret);
2112  }
2113  
2114  /*
2115   * sa_update_config()
2116   *
2117   * Used to update legacy files that need to be updated in bulk
2118   * Currently, this is a placeholder and will go away in a future
2119   * release.
2120   */
2121  
2122  int
2123  sa_update_config(sa_handle_t handle)
2124  {
2125  	/*
2126  	 * do legacy files first so we can tell when they change.
2127  	 * This will go away when we start updating individual records
2128  	 * rather than the whole file.
2129  	 */
2130  	update_legacy_config(handle);
2131  	return (SA_OK);
2132  }
2133  
2134  /*
2135   * get_node_attr(node, tag)
2136   *
2137   * Get the specified tag(attribute) if it exists on the node.  This is
2138   * used internally by a number of attribute oriented functions.
2139   */
2140  
2141  static char *
2142  get_node_attr(void *nodehdl, char *tag)
2143  {
2144  	xmlNodePtr node = (xmlNodePtr)nodehdl;
2145  	xmlChar *name = NULL;
2146  
2147  	if (node != NULL)
2148  		name = xmlGetProp(node, (xmlChar *)tag);
2149  	return ((char *)name);
2150  }
2151  
2152  /*
2153   * set_node_attr(node, tag)
2154   *
2155   * Set the specified tag(attribute) to the specified value This is
2156   * used internally by a number of attribute oriented functions. It
2157   * doesn't update the repository, only the internal document state.
2158   */
2159  
2160  void
2161  set_node_attr(void *nodehdl, char *tag, char *value)
2162  {
2163  	xmlNodePtr node = (xmlNodePtr)nodehdl;
2164  	if (node != NULL && tag != NULL) {
2165  		if (value != NULL)
2166  			(void) xmlSetProp(node, (xmlChar *)tag,
2167  			    (xmlChar *)value);
2168  		else
2169  			(void) xmlUnsetProp(node, (xmlChar *)tag);
2170  	}
2171  }
2172  
2173  /*
2174   * sa_get_group_attr(group, tag)
2175   *
2176   * Get the specied attribute, if defined, for the group.
2177   */
2178  
2179  char *
2180  sa_get_group_attr(sa_group_t group, char *tag)
2181  {
2182  	return (get_node_attr((void *)group, tag));
2183  }
2184  
2185  /*
2186   * sa_set_group_attr(group, tag, value)
2187   *
2188   * set the specified tag/attribute on the group using value as its
2189   * value.
2190   *
2191   * This will result in setting the property in the SMF repository as
2192   * well as in the internal document.
2193   */
2194  
2195  int
2196  sa_set_group_attr(sa_group_t group, char *tag, char *value)
2197  {
2198  	int ret;
2199  	char *groupname;
2200  	sa_handle_impl_t impl_handle;
2201  
2202  	/*
2203  	 * ZFS group/subgroup doesn't need the handle so shortcut.
2204  	 */
2205  	if (sa_group_is_zfs(group)) {
2206  		set_node_attr((void *)group, tag, value);
2207  		return (SA_OK);
2208  	}
2209  
2210  	impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2211  	if (impl_handle != NULL) {
2212  		groupname = sa_get_group_attr(group, "name");
2213  		ret = sa_get_instance(impl_handle->scfhandle, groupname);
2214  		if (ret == SA_OK) {
2215  			set_node_attr((void *)group, tag, value);
2216  			ret = sa_start_transaction(impl_handle->scfhandle,
2217  			    "operation");
2218  			if (ret == SA_OK) {
2219  				ret = sa_set_property(impl_handle->scfhandle,
2220  				    tag, value);
2221  				if (ret == SA_OK)
2222  					ret = sa_end_transaction(
2223  					    impl_handle->scfhandle,
2224  					    impl_handle);
2225  				else
2226  					sa_abort_transaction(
2227  					    impl_handle->scfhandle);
2228  			}
2229  			if (ret == SA_SYSTEM_ERR)
2230  				ret = SA_NO_PERMISSION;
2231  		}
2232  		if (groupname != NULL)
2233  			sa_free_attr_string(groupname);
2234  	} else {
2235  		ret = SA_SYSTEM_ERR;
2236  	}
2237  	return (ret);
2238  }
2239  
2240  /*
2241   * sa_get_share_attr(share, tag)
2242   *
2243   * Return the value of the tag/attribute set on the specified
2244   * share. Returns NULL if the tag doesn't exist.
2245   */
2246  
2247  char *
2248  sa_get_share_attr(sa_share_t share, char *tag)
2249  {
2250  	return (get_node_attr((void *)share, tag));
2251  }
2252  
2253  /*
2254   * _sa_set_share_description(share, description)
2255   *
2256   * Add a description tag with text contents to the specified share.  A
2257   * separate XML tag is used rather than a property. This can also be
2258   * used with resources.
2259   */
2260  
2261  xmlNodePtr
2262  _sa_set_share_description(void *share, char *content)
2263  {
2264  	xmlNodePtr node;
2265  	node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description",
2266  	    NULL);
2267  	xmlNodeSetContent(node, (xmlChar *)content);
2268  	return (node);
2269  }
2270  
2271  /*
2272   * sa_set_share_attr(share, tag, value)
2273   *
2274   * Set the share attribute specified by tag to the specified value. In
2275   * the case of "resource", enforce a no duplicates in a group rule. If
2276   * the share is not transient, commit the changes to the repository
2277   * else just update the share internally.
2278   */
2279  
2280  int
2281  sa_set_share_attr(sa_share_t share, char *tag, char *value)
2282  {
2283  	sa_group_t group;
2284  	sa_share_t resource;
2285  	int ret = SA_OK;
2286  
2287  	group = sa_get_parent_group(share);
2288  
2289  	/*
2290  	 * There are some attributes that may have specific
2291  	 * restrictions on them. Initially, only "resource" has
2292  	 * special meaning that needs to be checked. Only one instance
2293  	 * of a resource name may exist within a group.
2294  	 */
2295  
2296  	if (strcmp(tag, "resource") == 0) {
2297  		resource = sa_get_resource(group, value);
2298  		if (resource != share && resource != NULL)
2299  			ret = SA_DUPLICATE_NAME;
2300  	}
2301  	if (ret == SA_OK) {
2302  		set_node_attr((void *)share, tag, value);
2303  		if (group != NULL) {
2304  			char *type;
2305  			/* we can probably optimize this some */
2306  			type = sa_get_share_attr(share, "type");
2307  			if (type == NULL || strcmp(type, "transient") != 0) {
2308  				sa_handle_impl_t impl_handle;
2309  				impl_handle =
2310  				    (sa_handle_impl_t)sa_find_group_handle(
2311  				    group);
2312  				if (impl_handle != NULL) {
2313  					ret = sa_commit_share(
2314  					    impl_handle->scfhandle, group,
2315  					    share);
2316  				} else {
2317  					ret = SA_SYSTEM_ERR;
2318  				}
2319  			}
2320  			if (type != NULL)
2321  				sa_free_attr_string(type);
2322  		}
2323  	}
2324  	return (ret);
2325  }
2326  
2327  /*
2328   * sa_get_property_attr(prop, tag)
2329   *
2330   * Get the value of the specified property attribute. Standard
2331   * attributes are "type" and "value".
2332   */
2333  
2334  char *
2335  sa_get_property_attr(sa_property_t prop, char *tag)
2336  {
2337  	return (get_node_attr((void *)prop, tag));
2338  }
2339  
2340  /*
2341   * sa_get_optionset_attr(prop, tag)
2342   *
2343   * Get the value of the specified property attribute. Standard
2344   * attribute is "type".
2345   */
2346  
2347  char *
2348  sa_get_optionset_attr(sa_property_t optionset, char *tag)
2349  {
2350  	return (get_node_attr((void *)optionset, tag));
2351  
2352  }
2353  
2354  /*
2355   * sa_set_optionset_attr(optionset, tag, value)
2356   *
2357   * Set the specified attribute(tag) to the specified value on the
2358   * optionset.
2359   */
2360  
2361  void
2362  sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value)
2363  {
2364  	set_node_attr((void *)optionset, tag, value);
2365  }
2366  
2367  /*
2368   * sa_free_attr_string(string)
2369   *
2370   * Free the string that was returned in one of the sa_get_*_attr()
2371   * functions.
2372   */
2373  
2374  void
2375  sa_free_attr_string(char *string)
2376  {
2377  	xmlFree((xmlChar *)string);
2378  }
2379  
2380  /*
2381   * sa_get_optionset(group, proto)
2382   *
2383   * Return the optionset, if it exists, that is associated with the
2384   * specified protocol.
2385   */
2386  
2387  sa_optionset_t
2388  sa_get_optionset(void *group, char *proto)
2389  {
2390  	xmlNodePtr node;
2391  	xmlChar *value = NULL;
2392  
2393  	for (node = ((xmlNodePtr)group)->children; node != NULL;
2394  	    node = node->next) {
2395  		if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2396  			value = xmlGetProp(node, (xmlChar *)"type");
2397  			if (proto != NULL) {
2398  				if (value != NULL &&
2399  				    xmlStrcmp(value, (xmlChar *)proto) == 0) {
2400  					break;
2401  				}
2402  				if (value != NULL) {
2403  					xmlFree(value);
2404  					value = NULL;
2405  				}
2406  			} else {
2407  				break;
2408  			}
2409  		}
2410  	}
2411  	if (value != NULL)
2412  		xmlFree(value);
2413  	return ((sa_optionset_t)node);
2414  }
2415  
2416  /*
2417   * sa_get_next_optionset(optionset)
2418   *
2419   * Return the next optionset in the group. NULL if this was the last.
2420   */
2421  
2422  sa_optionset_t
2423  sa_get_next_optionset(sa_optionset_t optionset)
2424  {
2425  	xmlNodePtr node;
2426  
2427  	for (node = ((xmlNodePtr)optionset)->next; node != NULL;
2428  	    node = node->next) {
2429  		if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2430  			break;
2431  		}
2432  	}
2433  	return ((sa_optionset_t)node);
2434  }
2435  
2436  /*
2437   * sa_get_security(group, sectype, proto)
2438   *
2439   * Return the security optionset. The internal name is a hold over
2440   * from the implementation and will be changed before the API is
2441   * finalized. This is really a named optionset that can be negotiated
2442   * as a group of properties (like NFS security options).
2443   */
2444  
2445  sa_security_t
2446  sa_get_security(sa_group_t group, char *sectype, char *proto)
2447  {
2448  	xmlNodePtr node;
2449  	xmlChar *value = NULL;
2450  
2451  	for (node = ((xmlNodePtr)group)->children; node != NULL;
2452  	    node = node->next) {
2453  		if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2454  			if (proto != NULL) {
2455  				value = xmlGetProp(node, (xmlChar *)"type");
2456  				if (value == NULL ||
2457  				    (value != NULL &&
2458  				    xmlStrcmp(value, (xmlChar *)proto) != 0)) {
2459  					/* it doesn't match so continue */
2460  					xmlFree(value);
2461  					value = NULL;
2462  					continue;
2463  				}
2464  			}
2465  			if (value != NULL) {
2466  				xmlFree(value);
2467  				value = NULL;
2468  			}
2469  			/* potential match */
2470  			if (sectype != NULL) {
2471  				value = xmlGetProp(node, (xmlChar *)"sectype");
2472  				if (value != NULL &&
2473  				    xmlStrcmp(value, (xmlChar *)sectype) == 0) {
2474  					break;
2475  				}
2476  			} else {
2477  				break;
2478  			}
2479  		}
2480  		if (value != NULL) {
2481  			xmlFree(value);
2482  			value = NULL;
2483  		}
2484  	}
2485  	if (value != NULL)
2486  		xmlFree(value);
2487  	return ((sa_security_t)node);
2488  }
2489  
2490  /*
2491   * sa_get_next_security(security)
2492   *
2493   * Get the next security optionset if one exists.
2494   */
2495  
2496  sa_security_t
2497  sa_get_next_security(sa_security_t security)
2498  {
2499  	xmlNodePtr node;
2500  
2501  	for (node = ((xmlNodePtr)security)->next; node != NULL;
2502  	    node = node->next) {
2503  		if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2504  			break;
2505  		}
2506  	}
2507  	return ((sa_security_t)node);
2508  }
2509  
2510  /*
2511   * sa_get_property(optionset, prop)
2512   *
2513   * Get the property object with the name specified in prop from the
2514   * optionset.
2515   */
2516  
2517  sa_property_t
2518  sa_get_property(sa_optionset_t optionset, char *prop)
2519  {
2520  	xmlNodePtr node = (xmlNodePtr)optionset;
2521  	xmlChar *value = NULL;
2522  
2523  	if (optionset == NULL)
2524  		return (NULL);
2525  
2526  	for (node = node->children; node != NULL;
2527  	    node = node->next) {
2528  		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2529  			if (prop == NULL)
2530  				break;
2531  			value = xmlGetProp(node, (xmlChar *)"type");
2532  			if (value != NULL &&
2533  			    xmlStrcmp(value, (xmlChar *)prop) == 0) {
2534  				break;
2535  			}
2536  			if (value != NULL) {
2537  				xmlFree(value);
2538  				value = NULL;
2539  			}
2540  		}
2541  	}
2542  	if (value != NULL)
2543  		xmlFree(value);
2544  	if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
2545  		/*
2546  		 * avoid a non option node -- it is possible to be a
2547  		 * text node
2548  		 */
2549  		node = NULL;
2550  	}
2551  	return ((sa_property_t)node);
2552  }
2553  
2554  /*
2555   * sa_get_next_property(property)
2556   *
2557   * Get the next property following the specified property. NULL if
2558   * this was the last.
2559   */
2560  
2561  sa_property_t
2562  sa_get_next_property(sa_property_t property)
2563  {
2564  	xmlNodePtr node;
2565  
2566  	for (node = ((xmlNodePtr)property)->next; node != NULL;
2567  	    node = node->next) {
2568  		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2569  			break;
2570  		}
2571  	}
2572  	return ((sa_property_t)node);
2573  }
2574  
2575  /*
2576   * sa_set_share_description(share, content)
2577   *
2578   * Set the description of share to content.
2579   */
2580  
2581  int
2582  sa_set_share_description(sa_share_t share, char *content)
2583  {
2584  	xmlNodePtr node;
2585  	sa_group_t group;
2586  	int ret = SA_OK;
2587  
2588  	for (node = ((xmlNodePtr)share)->children; node != NULL;
2589  	    node = node->next) {
2590  		if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2591  			break;
2592  		}
2593  	}
2594  	/* no existing description but want to add */
2595  	if (node == NULL && content != NULL) {
2596  		/* add a description */
2597  		node = _sa_set_share_description(share, content);
2598  	} else if (node != NULL && content != NULL) {
2599  		/* update a description */
2600  		xmlNodeSetContent(node, (xmlChar *)content);
2601  	} else if (node != NULL && content == NULL) {
2602  		/* remove an existing description */
2603  		xmlUnlinkNode(node);
2604  		xmlFreeNode(node);
2605  	}
2606  	group = sa_get_parent_group(share);
2607  	if (group != NULL &&
2608  	    sa_is_persistent(share) && (!sa_group_is_zfs(group))) {
2609  		sa_handle_impl_t impl_handle;
2610  		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2611  		if (impl_handle != NULL) {
2612  			ret = sa_commit_share(impl_handle->scfhandle, group,
2613  			    share);
2614  		} else {
2615  			ret = SA_SYSTEM_ERR;
2616  		}
2617  	}
2618  	return (ret);
2619  }
2620  
2621  /*
2622   * fixproblemchars(string)
2623   *
2624   * don't want any newline or tab characters in the text since these
2625   * could break display of data and legacy file formats.
2626   */
2627  static void
2628  fixproblemchars(char *str)
2629  {
2630  	int c;
2631  	for (c = *str; c != '\0'; c = *++str) {
2632  		if (c == '\t' || c == '\n')
2633  			*str = ' ';
2634  		else if (c == '"')
2635  			*str = '\'';
2636  	}
2637  }
2638  
2639  /*
2640   * sa_get_share_description(share)
2641   *
2642   * Return the description text for the specified share if it
2643   * exists. NULL if no description exists.
2644   */
2645  
2646  char *
2647  sa_get_share_description(sa_share_t share)
2648  {
2649  	xmlChar *description = NULL;
2650  	xmlNodePtr node;
2651  
2652  	for (node = ((xmlNodePtr)share)->children; node != NULL;
2653  	    node = node->next) {
2654  		if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2655  			break;
2656  		}
2657  	}
2658  	if (node != NULL) {
2659  		description = xmlNodeGetContent(node);
2660  		fixproblemchars((char *)description);
2661  	}
2662  	return ((char *)description);
2663  }
2664  
2665  /*
2666   * sa_free(share_description(description)
2667   *
2668   * Free the description string.
2669   */
2670  
2671  void
2672  sa_free_share_description(char *description)
2673  {
2674  	xmlFree((xmlChar *)description);
2675  }
2676  
2677  /*
2678   * sa_create_optionset(group, proto)
2679   *
2680   * Create an optionset for the specified protocol in the specied
2681   * group. This is manifested as a property group within SMF.
2682   */
2683  
2684  sa_optionset_t
2685  sa_create_optionset(sa_group_t group, char *proto)
2686  {
2687  	sa_optionset_t optionset;
2688  	sa_group_t parent = group;
2689  	sa_share_t share = NULL;
2690  	int err = SA_OK;
2691  	char *id = NULL;
2692  
2693  	optionset = sa_get_optionset(group, proto);
2694  	if (optionset != NULL) {
2695  		/* can't have a duplicate protocol */
2696  		optionset = NULL;
2697  	} else {
2698  		/*
2699  		 * Account for resource names being slightly
2700  		 * different.
2701  		 */
2702  		if (sa_is_share(group)) {
2703  			/*
2704  			 * Transient shares do not have an "id" so not an
2705  			 * error to not find one.
2706  			 */
2707  			id = sa_get_share_attr((sa_share_t)group, "id");
2708  		} else if (sa_is_resource(group)) {
2709  			share = sa_get_resource_parent(
2710  			    (sa_resource_t)group);
2711  			id = sa_get_resource_attr(share, "id");
2712  
2713  			/* id can be NULL if the group is transient (ZFS) */
2714  			if (id == NULL && sa_is_persistent(group))
2715  				err = SA_NO_MEMORY;
2716  		}
2717  		if (err == SA_NO_MEMORY) {
2718  			/*
2719  			 * Couldn't get the id for the share or
2720  			 * resource. While this could be a
2721  			 * configuration issue, it is most likely an
2722  			 * out of memory. In any case, fail the create.
2723  			 */
2724  			return (NULL);
2725  		}
2726  
2727  		optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group,
2728  		    NULL, (xmlChar *)"optionset", NULL);
2729  		/*
2730  		 * only put to repository if on a group and we were
2731  		 * able to create an optionset.
2732  		 */
2733  		if (optionset != NULL) {
2734  			char oname[SA_STRSIZE];
2735  			char *groupname;
2736  
2737  			/*
2738  			 * Need to get parent group in all cases, but also get
2739  			 * the share if this is a resource.
2740  			 */
2741  			if (sa_is_share(group)) {
2742  				parent = sa_get_parent_group((sa_share_t)group);
2743  			} else if (sa_is_resource(group)) {
2744  				share = sa_get_resource_parent(
2745  				    (sa_resource_t)group);
2746  				parent = sa_get_parent_group(share);
2747  			}
2748  
2749  			sa_set_optionset_attr(optionset, "type", proto);
2750  
2751  			(void) sa_optionset_name(optionset, oname,
2752  			    sizeof (oname), id);
2753  			groupname = sa_get_group_attr(parent, "name");
2754  			if (groupname != NULL && sa_is_persistent(group)) {
2755  				sa_handle_impl_t impl_handle;
2756  				impl_handle =
2757  				    (sa_handle_impl_t)sa_find_group_handle(
2758  				    group);
2759  				assert(impl_handle != NULL);
2760  				if (impl_handle != NULL) {
2761  					(void) sa_get_instance(
2762  					    impl_handle->scfhandle, groupname);
2763  					(void) sa_create_pgroup(
2764  					    impl_handle->scfhandle, oname);
2765  				}
2766  			}
2767  			if (groupname != NULL)
2768  				sa_free_attr_string(groupname);
2769  		}
2770  	}
2771  
2772  	if (id != NULL)
2773  		sa_free_attr_string(id);
2774  	return (optionset);
2775  }
2776  
2777  /*
2778   * sa_get_property_parent(property)
2779   *
2780   * Given a property, return the object it is a property of. This will
2781   * be an optionset of some type.
2782   */
2783  
2784  static sa_optionset_t
2785  sa_get_property_parent(sa_property_t property)
2786  {
2787  	xmlNodePtr node = NULL;
2788  
2789  	if (property != NULL)
2790  		node = ((xmlNodePtr)property)->parent;
2791  	return ((sa_optionset_t)node);
2792  }
2793  
2794  /*
2795   * sa_get_optionset_parent(optionset)
2796   *
2797   * Return the parent of the specified optionset. This could be a group
2798   * or a share.
2799   */
2800  
2801  static sa_group_t
2802  sa_get_optionset_parent(sa_optionset_t optionset)
2803  {
2804  	xmlNodePtr node = NULL;
2805  
2806  	if (optionset != NULL)
2807  		node = ((xmlNodePtr)optionset)->parent;
2808  	return ((sa_group_t)node);
2809  }
2810  
2811  /*
2812   * zfs_needs_update(share)
2813   *
2814   * In order to avoid making multiple updates to a ZFS share when
2815   * setting properties, the share attribute "changed" will be set to
2816   * true when a property is added or modified.  When done adding
2817   * properties, we can then detect that an update is needed.  We then
2818   * clear the state here to detect additional changes.
2819   */
2820  
2821  static int
2822  zfs_needs_update(sa_share_t share)
2823  {
2824  	char *attr;
2825  	int result = 0;
2826  
2827  	attr = sa_get_share_attr(share, "changed");
2828  	if (attr != NULL) {
2829  		sa_free_attr_string(attr);
2830  		result = 1;
2831  	}
2832  	set_node_attr((void *)share, "changed", NULL);
2833  	return (result);
2834  }
2835  
2836  /*
2837   * zfs_set_update(share)
2838   *
2839   * Set the changed attribute of the share to true.
2840   */
2841  
2842  static void
2843  zfs_set_update(sa_share_t share)
2844  {
2845  	set_node_attr((void *)share, "changed", "true");
2846  }
2847  
2848  /*
2849   * sa_commit_properties(optionset, clear)
2850   *
2851   * Check if SMF or ZFS config and either update or abort the pending
2852   * changes.
2853   */
2854  
2855  int
2856  sa_commit_properties(sa_optionset_t optionset, int clear)
2857  {
2858  	sa_group_t group;
2859  	sa_group_t parent;
2860  	int zfs = 0;
2861  	int needsupdate = 0;
2862  	int ret = SA_OK;
2863  	sa_handle_impl_t impl_handle;
2864  
2865  	group = sa_get_optionset_parent(optionset);
2866  	if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) {
2867  		/* only update ZFS if on a share */
2868  		parent = sa_get_parent_group(group);
2869  		zfs++;
2870  		if (parent != NULL && is_zfs_group(parent))
2871  			needsupdate = zfs_needs_update(group);
2872  		else
2873  			zfs = 0;
2874  	}
2875  	if (zfs) {
2876  		if (!clear && needsupdate)
2877  			ret = sa_zfs_update((sa_share_t)group);
2878  	} else {
2879  		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2880  		if (impl_handle != NULL) {
2881  			if (clear) {
2882  				(void) sa_abort_transaction(
2883  				    impl_handle->scfhandle);
2884  			} else {
2885  				ret = sa_end_transaction(
2886  				    impl_handle->scfhandle, impl_handle);
2887  			}
2888  		} else {
2889  			ret = SA_SYSTEM_ERR;
2890  		}
2891  	}
2892  	return (ret);
2893  }
2894  
2895  /*
2896   * sa_destroy_optionset(optionset)
2897   *
2898   * Remove the optionset from its group. Update the repository to
2899   * reflect this change.
2900   */
2901  
2902  int
2903  sa_destroy_optionset(sa_optionset_t optionset)
2904  {
2905  	char name[SA_STRSIZE];
2906  	int len;
2907  	int ret;
2908  	char *id = NULL;
2909  	sa_group_t group;
2910  	int ispersist = 1;
2911  
2912  	/* now delete the prop group */
2913  	group = sa_get_optionset_parent(optionset);
2914  	if (group != NULL) {
2915  		if (sa_is_resource(group)) {
2916  			sa_resource_t resource = group;
2917  			sa_share_t share = sa_get_resource_parent(resource);
2918  			group = sa_get_parent_group(share);
2919  			id = sa_get_share_attr(share, "id");
2920  		} else if (sa_is_share(group)) {
2921  			id = sa_get_share_attr((sa_share_t)group, "id");
2922  		}
2923  		ispersist = sa_is_persistent(group);
2924  	}
2925  	if (ispersist) {
2926  		sa_handle_impl_t impl_handle;
2927  		len = sa_optionset_name(optionset, name, sizeof (name), id);
2928  		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2929  		if (impl_handle != NULL) {
2930  			if (len > 0) {
2931  				ret = sa_delete_pgroup(impl_handle->scfhandle,
2932  				    name);
2933  			}
2934  		} else {
2935  			ret = SA_SYSTEM_ERR;
2936  		}
2937  	}
2938  	xmlUnlinkNode((xmlNodePtr)optionset);
2939  	xmlFreeNode((xmlNodePtr)optionset);
2940  	if (id != NULL)
2941  		sa_free_attr_string(id);
2942  	return (ret);
2943  }
2944  
2945  /* private to the implementation */
2946  int
2947  _sa_remove_optionset(sa_optionset_t optionset)
2948  {
2949  	int ret = SA_OK;
2950  
2951  	xmlUnlinkNode((xmlNodePtr)optionset);
2952  	xmlFreeNode((xmlNodePtr)optionset);
2953  	return (ret);
2954  }
2955  
2956  /*
2957   * sa_create_security(group, sectype, proto)
2958   *
2959   * Create a security optionset (one that has a type name and a
2960   * proto). Security is left over from a pure NFS implementation. The
2961   * naming will change in the future when the API is released.
2962   */
2963  sa_security_t
2964  sa_create_security(sa_group_t group, char *sectype, char *proto)
2965  {
2966  	sa_security_t security;
2967  	char *id = NULL;
2968  	sa_group_t parent;
2969  	char *groupname = NULL;
2970  
2971  	if (group != NULL && sa_is_share(group)) {
2972  		id = sa_get_share_attr((sa_share_t)group, "id");
2973  		parent = sa_get_parent_group(group);
2974  		if (parent != NULL)
2975  			groupname = sa_get_group_attr(parent, "name");
2976  	} else if (group != NULL) {
2977  		groupname = sa_get_group_attr(group, "name");
2978  	}
2979  
2980  	security = sa_get_security(group, sectype, proto);
2981  	if (security != NULL) {
2982  		/* can't have a duplicate security option */
2983  		security = NULL;
2984  	} else {
2985  		security = (sa_security_t)xmlNewChild((xmlNodePtr)group,
2986  		    NULL, (xmlChar *)"security", NULL);
2987  		if (security != NULL) {
2988  			char oname[SA_STRSIZE];
2989  			sa_set_security_attr(security, "type", proto);
2990  
2991  			sa_set_security_attr(security, "sectype", sectype);
2992  			(void) sa_security_name(security, oname,
2993  			    sizeof (oname), id);
2994  			if (groupname != NULL && sa_is_persistent(group)) {
2995  				sa_handle_impl_t impl_handle;
2996  				impl_handle =
2997  				    (sa_handle_impl_t)sa_find_group_handle(
2998  				    group);
2999  				if (impl_handle != NULL) {
3000  					(void) sa_get_instance(
3001  					    impl_handle->scfhandle, groupname);
3002  					(void) sa_create_pgroup(
3003  					    impl_handle->scfhandle, oname);
3004  				}
3005  			}
3006  		}
3007  	}
3008  	if (id != NULL)
3009  		sa_free_attr_string(id);
3010  	if (groupname != NULL)
3011  		sa_free_attr_string(groupname);
3012  	return (security);
3013  }
3014  
3015  /*
3016   * sa_destroy_security(security)
3017   *
3018   * Remove the specified optionset from the document and the
3019   * configuration.
3020   */
3021  
3022  int
3023  sa_destroy_security(sa_security_t security)
3024  {
3025  	char name[SA_STRSIZE];
3026  	int len;
3027  	int ret = SA_OK;
3028  	char *id = NULL;
3029  	sa_group_t group;
3030  	int iszfs = 0;
3031  	int ispersist = 1;
3032  
3033  	group = sa_get_optionset_parent(security);
3034  
3035  	if (group != NULL)
3036  		iszfs = sa_group_is_zfs(group);
3037  
3038  	if (group != NULL && !iszfs) {
3039  		if (sa_is_share(group))
3040  			ispersist = sa_is_persistent(group);
3041  		id = sa_get_share_attr((sa_share_t)group, "id");
3042  	}
3043  	if (ispersist) {
3044  		len = sa_security_name(security, name, sizeof (name), id);
3045  		if (!iszfs && len > 0) {
3046  			sa_handle_impl_t impl_handle;
3047  			impl_handle =
3048  			    (sa_handle_impl_t)sa_find_group_handle(group);
3049  			if (impl_handle != NULL) {
3050  				ret = sa_delete_pgroup(impl_handle->scfhandle,
3051  				    name);
3052  			} else {
3053  				ret = SA_SYSTEM_ERR;
3054  			}
3055  		}
3056  	}
3057  	xmlUnlinkNode((xmlNodePtr)security);
3058  	xmlFreeNode((xmlNodePtr)security);
3059  	if (iszfs)
3060  		ret = sa_zfs_update(group);
3061  	if (id != NULL)
3062  		sa_free_attr_string(id);
3063  	return (ret);
3064  }
3065  
3066  /*
3067   * sa_get_security_attr(optionset, tag)
3068   *
3069   * Return the specified attribute value from the optionset.
3070   */
3071  
3072  char *
3073  sa_get_security_attr(sa_property_t optionset, char *tag)
3074  {
3075  	return (get_node_attr((void *)optionset, tag));
3076  
3077  }
3078  
3079  /*
3080   * sa_set_security_attr(optionset, tag, value)
3081   *
3082   * Set the optioset attribute specied by tag to the specified value.
3083   */
3084  
3085  void
3086  sa_set_security_attr(sa_group_t optionset, char *tag, char *value)
3087  {
3088  	set_node_attr((void *)optionset, tag, value);
3089  }
3090  
3091  /*
3092   * is_nodetype(node, type)
3093   *
3094   * Check to see if node is of the type specified.
3095   */
3096  
3097  static int
3098  is_nodetype(void *node, char *type)
3099  {
3100  	return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0);
3101  }
3102  
3103  /*
3104   * add_or_update()
3105   *
3106   * Add or update a property. Pulled out of sa_set_prop_by_prop for
3107   * readability.
3108   */
3109  static int
3110  add_or_update(scfutilhandle_t *scf_handle, int type, scf_value_t *value,
3111      scf_transaction_entry_t *entry, char *name, char *valstr)
3112  {
3113  	int ret = SA_SYSTEM_ERR;
3114  
3115  	if (value != NULL) {
3116  		if (type == SA_PROP_OP_ADD)
3117  			ret = scf_transaction_property_new(scf_handle->trans,
3118  			    entry, name, SCF_TYPE_ASTRING);
3119  		else
3120  			ret = scf_transaction_property_change(scf_handle->trans,
3121  			    entry, name, SCF_TYPE_ASTRING);
3122  		if (ret == 0) {
3123  			ret = scf_value_set_astring(value, valstr);
3124  			if (ret == 0)
3125  				ret = scf_entry_add_value(entry, value);
3126  			if (ret == 0)
3127  				return (ret);
3128  			scf_value_destroy(value);
3129  		} else {
3130  			scf_entry_destroy(entry);
3131  		}
3132  	}
3133  	return (SA_SYSTEM_ERR);
3134  }
3135  
3136  /*
3137   * sa_set_prop_by_prop(optionset, group, prop, type)
3138   *
3139   * Add/remove/update the specified property prop into the optionset or
3140   * share. If a share, sort out which property group based on GUID. In
3141   * all cases, the appropriate transaction is set (or ZFS share is
3142   * marked as needing an update)
3143   */
3144  
3145  static int
3146  sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
3147      sa_property_t prop, int type)
3148  {
3149  	char *name;
3150  	char *valstr;
3151  	int ret = SA_OK;
3152  	scf_transaction_entry_t *entry;
3153  	scf_value_t *value;
3154  	int opttype; /* 1 == optionset, 0 == security */
3155  	char *id = NULL;
3156  	int iszfs = 0;
3157  	sa_group_t parent = NULL;
3158  	sa_share_t share = NULL;
3159  	sa_handle_impl_t impl_handle;
3160  	scfutilhandle_t  *scf_handle;
3161  
3162  	if (!sa_is_persistent(group)) {
3163  		/*
3164  		 * if the group/share is not persistent we don't need
3165  		 * to do anything here
3166  		 */
3167  		return (SA_OK);
3168  	}
3169  	impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
3170  	if (impl_handle == NULL || impl_handle->scfhandle == NULL)
3171  		return (SA_SYSTEM_ERR);
3172  	scf_handle = impl_handle->scfhandle;
3173  	name = sa_get_property_attr(prop, "type");
3174  	valstr = sa_get_property_attr(prop, "value");
3175  	entry = scf_entry_create(scf_handle->handle);
3176  	opttype = is_nodetype((void *)optionset, "optionset");
3177  
3178  	/*
3179  	 * Check for share vs. resource since they need slightly
3180  	 * different treatment given the hierarchy.
3181  	 */
3182  	if (valstr != NULL && entry != NULL) {
3183  		if (sa_is_share(group)) {
3184  			parent = sa_get_parent_group(group);
3185  			share = (sa_share_t)group;
3186  			if (parent != NULL)
3187  				iszfs = is_zfs_group(parent);
3188  		} else if (sa_is_resource(group)) {
3189  			share = sa_get_parent_group(group);
3190  			if (share != NULL)
3191  				parent = sa_get_parent_group(share);
3192  		} else {
3193  			iszfs = is_zfs_group(group);
3194  		}
3195  		if (!iszfs) {
3196  			if (scf_handle->trans == NULL) {
3197  				char oname[SA_STRSIZE];
3198  				char *groupname = NULL;
3199  				if (share != NULL) {
3200  					if (parent != NULL)
3201  						groupname =
3202  						    sa_get_group_attr(parent,
3203  						    "name");
3204  					id = sa_get_share_attr(
3205  					    (sa_share_t)share, "id");
3206  				} else {
3207  					groupname = sa_get_group_attr(group,
3208  					    "name");
3209  				}
3210  				if (groupname != NULL) {
3211  					ret = sa_get_instance(scf_handle,
3212  					    groupname);
3213  					sa_free_attr_string(groupname);
3214  				}
3215  				if (opttype)
3216  					(void) sa_optionset_name(optionset,
3217  					    oname, sizeof (oname), id);
3218  				else
3219  					(void) sa_security_name(optionset,
3220  					    oname, sizeof (oname), id);
3221  				ret = sa_start_transaction(scf_handle, oname);
3222  				if (id != NULL)
3223  					sa_free_attr_string(id);
3224  			}
3225  			if (ret == SA_OK) {
3226  				switch (type) {
3227  				case SA_PROP_OP_REMOVE:
3228  					ret = scf_transaction_property_delete(
3229  					    scf_handle->trans, entry, name);
3230  					break;
3231  				case SA_PROP_OP_ADD:
3232  				case SA_PROP_OP_UPDATE:
3233  					value = scf_value_create(
3234  					    scf_handle->handle);
3235  					ret = add_or_update(scf_handle, type,
3236  					    value, entry, name, valstr);
3237  					break;
3238  				}
3239  			}
3240  		} else {
3241  			/*
3242  			 * ZFS update. The calling function would have updated
3243  			 * the internal XML structure. Just need to flag it as
3244  			 * changed for ZFS.
3245  			 */
3246  			zfs_set_update((sa_share_t)group);
3247  		}
3248  	}
3249  
3250  	if (name != NULL)
3251  		sa_free_attr_string(name);
3252  	if (valstr != NULL)
3253  		sa_free_attr_string(valstr);
3254  	else if (entry != NULL)
3255  		scf_entry_destroy(entry);
3256  
3257  	if (ret == -1)
3258  		ret = SA_SYSTEM_ERR;
3259  
3260  	return (ret);
3261  }
3262  
3263  /*
3264   * sa_create_section(name, value)
3265   *
3266   * Create a new section with the specified name and extra data.
3267   */
3268  
3269  sa_property_t
3270  sa_create_section(char *name, char *extra)
3271  {
3272  	xmlNodePtr node;
3273  
3274  	node = xmlNewNode(NULL, (xmlChar *)"section");
3275  	if (node != NULL) {
3276  		if (name != NULL)
3277  			(void) xmlSetProp(node, (xmlChar *)"name",
3278  			    (xmlChar *)name);
3279  		if (extra != NULL)
3280  			(void) xmlSetProp(node, (xmlChar *)"extra",
3281  			    (xmlChar *)extra);
3282  	}
3283  	return ((sa_property_t)node);
3284  }
3285  
3286  void
3287  sa_set_section_attr(sa_property_t sect, char *name, char *value)
3288  {
3289  	(void) xmlSetProp(sect, (xmlChar *)name, (xmlChar *)value);
3290  }
3291  
3292  /*
3293   * sa_create_property(section, name, value)
3294   *
3295   * Create a new property with the specified name and value.
3296   */
3297  
3298  sa_property_t
3299  sa_create_property(char *name, char *value)
3300  {
3301  	xmlNodePtr node;
3302  
3303  	node = xmlNewNode(NULL, (xmlChar *)"option");
3304  	if (node != NULL) {
3305  		(void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name);
3306  		(void) xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value);
3307  	}
3308  	return ((sa_property_t)node);
3309  }
3310  
3311  /*
3312   * sa_add_property(object, property)
3313   *
3314   * Add the specified property to the object. Issue the appropriate
3315   * transaction or mark a ZFS object as needing an update.
3316   */
3317  
3318  int
3319  sa_add_property(void *object, sa_property_t property)
3320  {
3321  	int ret = SA_OK;
3322  	sa_group_t parent;
3323  	sa_group_t group;
3324  	char *proto;
3325  
3326  	if (property != NULL) {
3327  		sa_handle_t handle;
3328  		handle = sa_find_group_handle((sa_group_t)object);
3329  		/* It is legitimate to not find a handle */
3330  		proto = sa_get_optionset_attr(object, "type");
3331  		if ((ret = sa_valid_property(handle, object, proto,
3332  		    property)) == SA_OK) {
3333  			property = (sa_property_t)xmlAddChild(
3334  			    (xmlNodePtr)object, (xmlNodePtr)property);
3335  		} else {
3336  			if (proto != NULL)
3337  				sa_free_attr_string(proto);
3338  			return (ret);
3339  		}
3340  		if (proto != NULL)
3341  			sa_free_attr_string(proto);
3342  	}
3343  
3344  
3345  	parent = sa_get_parent_group(object);
3346  	if (!sa_is_persistent(parent))
3347  		return (ret);
3348  
3349  	if (sa_is_resource(parent)) {
3350  		/*
3351  		 * Resources are children of share.  Need to go up two
3352  		 * levels to find the group but the parent needs to be
3353  		 * the share at this point in order to get the "id".
3354  		 */
3355  		parent = sa_get_parent_group(parent);
3356  		group = sa_get_parent_group(parent);
3357  	} else if (sa_is_share(parent)) {
3358  		group = sa_get_parent_group(parent);
3359  	} else {
3360  		group = parent;
3361  	}
3362  
3363  	if (property == NULL) {
3364  		ret = SA_NO_MEMORY;
3365  	} else {
3366  		char oname[SA_STRSIZE];
3367  
3368  		if (!is_zfs_group(group)) {
3369  			char *id = NULL;
3370  			sa_handle_impl_t impl_handle;
3371  			scfutilhandle_t  *scf_handle;
3372  
3373  			impl_handle = (sa_handle_impl_t)sa_find_group_handle(
3374  			    group);
3375  			if (impl_handle == NULL ||
3376  			    impl_handle->scfhandle == NULL)
3377  				ret = SA_SYSTEM_ERR;
3378  			if (ret == SA_OK) {
3379  				scf_handle = impl_handle->scfhandle;
3380  				if (sa_is_share((sa_group_t)parent)) {
3381  					id = sa_get_share_attr(
3382  					    (sa_share_t)parent, "id");
3383  				}
3384  				if (scf_handle->trans == NULL) {
3385  					if (is_nodetype(object, "optionset")) {
3386  						(void) sa_optionset_name(
3387  						    (sa_optionset_t)object,
3388  						    oname, sizeof (oname), id);
3389  					} else {
3390  						(void) sa_security_name(
3391  						    (sa_optionset_t)object,
3392  						    oname, sizeof (oname), id);
3393  					}
3394  					ret = sa_start_transaction(scf_handle,
3395  					    oname);
3396  				}
3397  				if (ret == SA_OK) {
3398  					char *name;
3399  					char *value;
3400  					name = sa_get_property_attr(property,
3401  					    "type");
3402  					value = sa_get_property_attr(property,
3403  					    "value");
3404  					if (name != NULL && value != NULL) {
3405  						if (scf_handle->scf_state ==
3406  						    SCH_STATE_INIT) {
3407  							ret = sa_set_property(
3408  							    scf_handle, name,
3409  							    value);
3410  						}
3411  					} else {
3412  						ret = SA_CONFIG_ERR;
3413  					}
3414  					if (name != NULL)
3415  						sa_free_attr_string(
3416  						    name);
3417  					if (value != NULL)
3418  						sa_free_attr_string(value);
3419  				}
3420  				if (id != NULL)
3421  					sa_free_attr_string(id);
3422  			}
3423  		} else {
3424  			/*
3425  			 * ZFS is a special case. We do want
3426  			 * to allow editing property/security
3427  			 * lists since we can have a better
3428  			 * syntax and we also want to keep
3429  			 * things consistent when possible.
3430  			 *
3431  			 * Right now, we defer until the
3432  			 * sa_commit_properties so we can get
3433  			 * them all at once. We do need to
3434  			 * mark the share as "changed"
3435  			 */
3436  			zfs_set_update((sa_share_t)parent);
3437  		}
3438  	}
3439  	return (ret);
3440  }
3441  
3442  /*
3443   * sa_remove_property(property)
3444   *
3445   * Remove the specied property from its containing object. Update the
3446   * repository as appropriate.
3447   */
3448  
3449  int
3450  sa_remove_property(sa_property_t property)
3451  {
3452  	int ret = SA_OK;
3453  
3454  	if (property != NULL) {
3455  		sa_optionset_t optionset;
3456  		sa_group_t group;
3457  		optionset = sa_get_property_parent(property);
3458  		if (optionset != NULL) {
3459  			group = sa_get_optionset_parent(optionset);
3460  			if (group != NULL) {
3461  				ret = sa_set_prop_by_prop(optionset, group,
3462  				    property, SA_PROP_OP_REMOVE);
3463  			}
3464  		}
3465  		xmlUnlinkNode((xmlNodePtr)property);
3466  		xmlFreeNode((xmlNodePtr)property);
3467  	} else {
3468  		ret = SA_NO_SUCH_PROP;
3469  	}
3470  	return (ret);
3471  }
3472  
3473  /*
3474   * sa_update_property(property, value)
3475   *
3476   * Update the specified property to the new value.  If value is NULL,
3477   * we currently treat this as a remove.
3478   */
3479  
3480  int
3481  sa_update_property(sa_property_t property, char *value)
3482  {
3483  	int ret = SA_OK;
3484  	if (value == NULL) {
3485  		return (sa_remove_property(property));
3486  	} else {
3487  		sa_optionset_t optionset;
3488  		sa_group_t group;
3489  		set_node_attr((void *)property, "value", value);
3490  		optionset = sa_get_property_parent(property);
3491  		if (optionset != NULL) {
3492  			group = sa_get_optionset_parent(optionset);
3493  			if (group != NULL) {
3494  				ret = sa_set_prop_by_prop(optionset, group,
3495  				    property, SA_PROP_OP_UPDATE);
3496  			}
3497  		} else {
3498  			ret = SA_NO_SUCH_PROP;
3499  		}
3500  	}
3501  	return (ret);
3502  }
3503  
3504  /*
3505   * sa_get_protocol_section(propset, prop)
3506   *
3507   * Get the specified protocol specific section. These are global to
3508   * the protocol and not specific to a group or share.
3509   */
3510  
3511  sa_protocol_properties_t
3512  sa_get_protocol_section(sa_protocol_properties_t propset, char *section)
3513  {
3514  	xmlNodePtr node = (xmlNodePtr)propset;
3515  	xmlChar *value = NULL;
3516  	char *proto;
3517  
3518  	proto = sa_get_optionset_attr(propset, "type");
3519  	if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) {
3520  		if (proto != NULL)
3521  			sa_free_attr_string(proto);
3522  		return (propset);
3523  	}
3524  
3525  	for (node = node->children; node != NULL;
3526  	    node = node->next) {
3527  		if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) {
3528  			if (section == NULL)
3529  				break;
3530  			value = xmlGetProp(node, (xmlChar *)"name");
3531  			if (value != NULL &&
3532  			    xmlStrcasecmp(value, (xmlChar *)section) == 0) {
3533  				break;
3534  			}
3535  			if (value != NULL) {
3536  				xmlFree(value);
3537  				value = NULL;
3538  			}
3539  		}
3540  	}
3541  	if (value != NULL)
3542  		xmlFree(value);
3543  	if (proto != NULL)
3544  		sa_free_attr_string(proto);
3545  	if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"section") != 0) {
3546  		/*
3547  		 * avoid a non option node -- it is possible to be a
3548  		 * text node
3549  		 */
3550  		node = NULL;
3551  	}
3552  	return ((sa_protocol_properties_t)node);
3553  }
3554  
3555  /*
3556   * sa_get_next_protocol_section(prop, find)
3557   *
3558   * Get the next protocol specific section in the list.
3559   */
3560  
3561  sa_property_t
3562  sa_get_next_protocol_section(sa_property_t prop, char *find)
3563  {
3564  	xmlNodePtr node;
3565  	xmlChar *value = NULL;
3566  	char *proto;
3567  
3568  	proto = sa_get_optionset_attr(prop, "type");
3569  	if ((sa_proto_get_featureset(proto) & SA_FEATURE_HAS_SECTIONS) == 0) {
3570  		if (proto != NULL)
3571  			sa_free_attr_string(proto);
3572  		return ((sa_property_t)NULL);
3573  	}
3574  
3575  	for (node = ((xmlNodePtr)prop)->next; node != NULL;
3576  	    node = node->next) {
3577  		if (xmlStrcmp(node->name, (xmlChar *)"section") == 0) {
3578  			if (find == NULL)
3579  				break;
3580  			value = xmlGetProp(node, (xmlChar *)"name");
3581  			if (value != NULL &&
3582  			    xmlStrcasecmp(value, (xmlChar *)find) == 0) {
3583  				break;
3584  			}
3585  			if (value != NULL) {
3586  				xmlFree(value);
3587  				value = NULL;
3588  			}
3589  
3590  		}
3591  	}
3592  	if (value != NULL)
3593  		xmlFree(value);
3594  	if (proto != NULL)
3595  		sa_free_attr_string(proto);
3596  	return ((sa_property_t)node);
3597  }
3598  
3599  /*
3600   * sa_get_protocol_property(propset, prop)
3601   *
3602   * Get the specified protocol specific property. These are global to
3603   * the protocol and not specific to a group or share.
3604   */
3605  
3606  sa_property_t
3607  sa_get_protocol_property(sa_protocol_properties_t propset, char *prop)
3608  {
3609  	xmlNodePtr node = (xmlNodePtr)propset;
3610  	xmlChar *value = NULL;
3611  
3612  	if (propset == NULL)
3613  		return (NULL);
3614  
3615  	for (node = node->children; node != NULL;
3616  	    node = node->next) {
3617  		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3618  			if (prop == NULL)
3619  				break;
3620  			value = xmlGetProp(node, (xmlChar *)"type");
3621  			if (value != NULL &&
3622  			    xmlStrcasecmp(value, (xmlChar *)prop) == 0) {
3623  				break;
3624  			}
3625  			if (value != NULL) {
3626  				xmlFree(value);
3627  				value = NULL;
3628  			}
3629  		}
3630  	}
3631  	if (value != NULL)
3632  		xmlFree(value);
3633  	if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
3634  		/*
3635  		 * avoid a non option node -- it is possible to be a
3636  		 * text node
3637  		 */
3638  		node = NULL;
3639  	}
3640  	return ((sa_property_t)node);
3641  }
3642  
3643  /*
3644   * sa_get_next_protocol_property(prop)
3645   *
3646   * Get the next protocol specific property in the list.
3647   */
3648  
3649  sa_property_t
3650  sa_get_next_protocol_property(sa_property_t prop, char *find)
3651  {
3652  	xmlNodePtr node;
3653  	xmlChar *value = NULL;
3654  
3655  	for (node = ((xmlNodePtr)prop)->next; node != NULL;
3656  	    node = node->next) {
3657  		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3658  			if (find == NULL)
3659  				break;
3660  			value = xmlGetProp(node, (xmlChar *)"type");
3661  			if (value != NULL &&
3662  			    xmlStrcasecmp(value, (xmlChar *)find) == 0) {
3663  				break;
3664  			}
3665  			if (value != NULL) {
3666  				xmlFree(value);
3667  				value = NULL;
3668  			}
3669  
3670  		}
3671  	}
3672  	if (value != NULL)
3673  		xmlFree(value);
3674  	return ((sa_property_t)node);
3675  }
3676  
3677  /*
3678   * sa_set_protocol_property(prop, value)
3679   *
3680   * Set the specified property to have the new value.  The protocol
3681   * specific plugin will then be called to update the property.
3682   */
3683  
3684  int
3685  sa_set_protocol_property(sa_property_t prop, char *section, char *value)
3686  {
3687  	sa_protocol_properties_t propset;
3688  	char *proto;
3689  	int ret = SA_INVALID_PROTOCOL;
3690  
3691  	propset = ((xmlNodePtr)prop)->parent;
3692  	if (propset != NULL) {
3693  		proto = sa_get_optionset_attr(propset, "type");
3694  		if (proto != NULL) {
3695  			if (section != NULL)
3696  				set_node_attr((xmlNodePtr)prop, "section",
3697  				    section);
3698  			set_node_attr((xmlNodePtr)prop, "value", value);
3699  			ret = sa_proto_set_property(proto, prop);
3700  			sa_free_attr_string(proto);
3701  		}
3702  	}
3703  	return (ret);
3704  }
3705  
3706  /*
3707   * sa_add_protocol_property(propset, prop)
3708   *
3709   * Add a new property to the protocol specific property set.
3710   */
3711  
3712  int
3713  sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop)
3714  {
3715  	xmlNodePtr node;
3716  
3717  	/* should check for legitimacy */
3718  	node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop);
3719  	if (node != NULL)
3720  		return (SA_OK);
3721  	return (SA_NO_MEMORY);
3722  }
3723  
3724  /*
3725   * sa_create_protocol_properties(proto)
3726   *
3727   * Create a protocol specific property set.
3728   */
3729  
3730  sa_protocol_properties_t
3731  sa_create_protocol_properties(char *proto)
3732  {
3733  	xmlNodePtr node;
3734  
3735  	node = xmlNewNode(NULL, (xmlChar *)"propertyset");
3736  	if (node != NULL)
3737  		(void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
3738  	return (node);
3739  }
3740  
3741  /*
3742   * sa_get_share_resource(share, resource)
3743   *
3744   * Get the named resource from the share, if it exists. If resource is
3745   * NULL, get the first resource.
3746   */
3747  
3748  sa_resource_t
3749  sa_get_share_resource(sa_share_t share, char *resource)
3750  {
3751  	xmlNodePtr node = NULL;
3752  	xmlChar *name;
3753  
3754  	if (share != NULL) {
3755  		for (node = ((xmlNodePtr)share)->children; node != NULL;
3756  		    node = node->next) {
3757  			if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) {
3758  				if (resource == NULL) {
3759  					/*
3760  					 * We are looking for the first
3761  					 * resource node and not a names
3762  					 * resource.
3763  					 */
3764  					break;
3765  				} else {
3766  					/* is it the correct share? */
3767  					name = xmlGetProp(node,
3768  					    (xmlChar *)"name");
3769  					if (name != NULL &&
3770  					    xmlStrcasecmp(name,
3771  					    (xmlChar *)resource) == 0) {
3772  						xmlFree(name);
3773  						break;
3774  					}
3775  					xmlFree(name);
3776  				}
3777  			}
3778  		}
3779  	}
3780  	return ((sa_resource_t)node);
3781  }
3782  
3783  /*
3784   * sa_get_next_resource(resource)
3785   *	Return the next share following the specified share
3786   *	from the internal list of shares. Returns NULL if there
3787   *	are no more shares.  The list is relative to the same
3788   *	group.
3789   */
3790  sa_share_t
3791  sa_get_next_resource(sa_resource_t resource)
3792  {
3793  	xmlNodePtr node = NULL;
3794  
3795  	if (resource != NULL) {
3796  		for (node = ((xmlNodePtr)resource)->next; node != NULL;
3797  		    node = node->next) {
3798  			if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0)
3799  				break;
3800  		}
3801  	}
3802  	return ((sa_share_t)node);
3803  }
3804  
3805  /*
3806   * _sa_get_next_resource_index(share)
3807   *
3808   * get the next resource index number (one greater then current largest)
3809   */
3810  
3811  static int
3812  _sa_get_next_resource_index(sa_share_t share)
3813  {
3814  	sa_resource_t resource;
3815  	int index = 0;
3816  	char *id;
3817  
3818  	for (resource = sa_get_share_resource(share, NULL);
3819  	    resource != NULL;
3820  	    resource = sa_get_next_resource(resource)) {
3821  		id = get_node_attr((void *)resource, "id");
3822  		if (id != NULL) {
3823  			int val;
3824  			val = atoi(id);
3825  			if (val > index)
3826  				index = val;
3827  			sa_free_attr_string(id);
3828  		}
3829  	}
3830  	return (index + 1);
3831  }
3832  
3833  
3834  /*
3835   * sa_add_resource(share, resource, persist, &err)
3836   *
3837   * Adds a new resource name associated with share. The resource name
3838   * must be unique in the system and will be case insensitive (eventually).
3839   */
3840  
3841  sa_resource_t
3842  sa_add_resource(sa_share_t share, char *resource, int persist, int *error)
3843  {
3844  	xmlNodePtr node;
3845  	int err = SA_OK;
3846  	sa_resource_t res;
3847  	sa_group_t group;
3848  	sa_handle_t handle;
3849  	char istring[8]; /* just big enough for an integer value */
3850  	int index;
3851  
3852  	group = sa_get_parent_group(share);
3853  	handle = sa_find_group_handle(group);
3854  	res = sa_find_resource(handle, resource);
3855  	if (res != NULL) {
3856  		err = SA_DUPLICATE_NAME;
3857  		res = NULL;
3858  	} else {
3859  		node = xmlNewChild((xmlNodePtr)share, NULL,
3860  		    (xmlChar *)"resource", NULL);
3861  		if (node != NULL) {
3862  			(void) xmlSetProp(node, (xmlChar *)"name",
3863  			    (xmlChar *)resource);
3864  			(void) xmlSetProp(node, (xmlChar *)"type", persist ?
3865  			    (xmlChar *)"persist" : (xmlChar *)"transient");
3866  			if (persist != SA_SHARE_TRANSIENT) {
3867  				index = _sa_get_next_resource_index(share);
3868  				(void) snprintf(istring, sizeof (istring), "%d",
3869  				    index);
3870  				(void) xmlSetProp(node, (xmlChar *)"id",
3871  				    (xmlChar *)istring);
3872  
3873  				if (!sa_is_persistent((sa_group_t)share))
3874  					goto done;
3875  
3876  				if (!sa_group_is_zfs(group)) {
3877  					/* ZFS doesn't use resource names */
3878  					sa_handle_impl_t ihandle;
3879  
3880  					ihandle = (sa_handle_impl_t)
3881  					    sa_find_group_handle(
3882  					    group);
3883  					if (ihandle != NULL)
3884  						err = sa_commit_share(
3885  						    ihandle->scfhandle, group,
3886  						    share);
3887  					else
3888  						err = SA_SYSTEM_ERR;
3889  				} else {
3890  					err = sa_zfs_update((sa_share_t)group);
3891  				}
3892  			}
3893  		}
3894  	}
3895  done:
3896  	if (error != NULL)
3897  		*error = err;
3898  	return ((sa_resource_t)node);
3899  }
3900  
3901  /*
3902   * sa_remove_resource(resource)
3903   *
3904   * Remove the resource name from the share (and the system)
3905   */
3906  
3907  int
3908  sa_remove_resource(sa_resource_t resource)
3909  {
3910  	sa_share_t share;
3911  	sa_group_t group;
3912  	char *type;
3913  	int ret = SA_OK;
3914  	boolean_t transient = B_FALSE;
3915  	sa_optionset_t opt;
3916  
3917  	share = sa_get_resource_parent(resource);
3918  	type = sa_get_share_attr(share, "type");
3919  	group = sa_get_parent_group(share);
3920  
3921  
3922  	if (type != NULL) {
3923  		if (strcmp(type, "persist") != 0)
3924  			transient = B_TRUE;
3925  		sa_free_attr_string(type);
3926  	}
3927  
3928  	/* Disable the resource for all protocols. */
3929  	(void) sa_disable_resource(resource, NULL);
3930  
3931  	/* Remove any optionsets from the resource. */
3932  	for (opt = sa_get_optionset(resource, NULL);
3933  	    opt != NULL;
3934  	    opt = sa_get_next_optionset(opt))
3935  		(void) sa_destroy_optionset(opt);
3936  
3937  	/* Remove from the share */
3938  	xmlUnlinkNode((xmlNode *)resource);
3939  	xmlFreeNode((xmlNode *)resource);
3940  
3941  	/* only do SMF action if permanent and not ZFS */
3942  	if (transient)
3943  		return (ret);
3944  
3945  	if (!sa_group_is_zfs(group)) {
3946  		sa_handle_impl_t ihandle;
3947  		ihandle = (sa_handle_impl_t)sa_find_group_handle(group);
3948  		if (ihandle != NULL)
3949  			ret = sa_commit_share(ihandle->scfhandle, group, share);
3950  		else
3951  			ret = SA_SYSTEM_ERR;
3952  	} else {
3953  		ret = sa_zfs_update((sa_share_t)group);
3954  	}
3955  
3956  	return (ret);
3957  }
3958  
3959  /*
3960   * proto_rename_resource(handle, group, resource, newname)
3961   *
3962   * Helper function for sa_rename_resource that notifies the protocol
3963   * of a resource name change prior to a config repository update.
3964   */
3965  static int
3966  proto_rename_resource(sa_handle_t handle, sa_group_t group,
3967      sa_resource_t resource, char *newname)
3968  {
3969  	sa_optionset_t optionset;
3970  	int ret = SA_OK;
3971  	int err;
3972  
3973  	for (optionset = sa_get_optionset(group, NULL);
3974  	    optionset != NULL;
3975  	    optionset = sa_get_next_optionset(optionset)) {
3976  		char *type;
3977  		type = sa_get_optionset_attr(optionset, "type");
3978  		if (type != NULL) {
3979  			err = sa_proto_rename_resource(handle, type, resource,
3980  			    newname);
3981  			if (err != SA_OK)
3982  				ret = err;
3983  			sa_free_attr_string(type);
3984  		}
3985  	}
3986  	return (ret);
3987  }
3988  
3989  /*
3990   * sa_rename_resource(resource, newname)
3991   *
3992   * Rename the resource to the new name, if it is unique.
3993   */
3994  
3995  int
3996  sa_rename_resource(sa_resource_t resource, char *newname)
3997  {
3998  	sa_share_t share;
3999  	sa_group_t group = NULL;
4000  	sa_resource_t target;
4001  	int ret = SA_CONFIG_ERR;
4002  	sa_handle_t handle = NULL;
4003  
4004  	share = sa_get_resource_parent(resource);
4005  	if (share == NULL)
4006  		return (ret);
4007  
4008  	group = sa_get_parent_group(share);
4009  	if (group == NULL)
4010  		return (ret);
4011  
4012  	handle = (sa_handle_impl_t)sa_find_group_handle(group);
4013  	if (handle == NULL)
4014  		return (ret);
4015  
4016  	target = sa_find_resource(handle, newname);
4017  	if (target != NULL) {
4018  		ret = SA_DUPLICATE_NAME;
4019  	} else {
4020  		/*
4021  		 * Everything appears to be valid at this
4022  		 * point. Change the name of the active share and then
4023  		 * update the share in the appropriate repository.
4024  		 */
4025  		ret = proto_rename_resource(handle, group, resource, newname);
4026  		set_node_attr(resource, "name", newname);
4027  
4028  		if (!sa_is_persistent((sa_group_t)share))
4029  			return (ret);
4030  
4031  		if (!sa_group_is_zfs(group)) {
4032  			sa_handle_impl_t ihandle = (sa_handle_impl_t)handle;
4033  			ret = sa_commit_share(ihandle->scfhandle, group,
4034  			    share);
4035  		} else {
4036  			ret = sa_zfs_update((sa_share_t)group);
4037  		}
4038  	}
4039  	return (ret);
4040  }
4041  
4042  /*
4043   * sa_get_resource_attr(resource, tag)
4044   *
4045   * Get the named attribute of the resource. "name" and "id" are
4046   * currently defined.  NULL if tag not defined.
4047   */
4048  
4049  char *
4050  sa_get_resource_attr(sa_resource_t resource, char *tag)
4051  {
4052  	return (get_node_attr((void *)resource, tag));
4053  }
4054  
4055  /*
4056   * sa_set_resource_attr(resource, tag, value)
4057   *
4058   * Get the named attribute of the resource. "name" and "id" are
4059   * currently defined.  NULL if tag not defined. Currently we don't do
4060   * much, but additional checking may be needed in the future.
4061   */
4062  
4063  int
4064  sa_set_resource_attr(sa_resource_t resource, char *tag, char *value)
4065  {
4066  	set_node_attr((void *)resource, tag, value);
4067  	return (SA_OK);
4068  }
4069  
4070  /*
4071   * sa_get_resource_parent(resource_t)
4072   *
4073   * Returns the share associated with the resource.
4074   */
4075  
4076  sa_share_t
4077  sa_get_resource_parent(sa_resource_t resource)
4078  {
4079  	sa_share_t share = NULL;
4080  
4081  	if (resource != NULL)
4082  		share = (sa_share_t)((xmlNodePtr)resource)->parent;
4083  	return (share);
4084  }
4085  
4086  /*
4087   * find_resource(group, name)
4088   *
4089   * Find the resource within the group.
4090   */
4091  
4092  static sa_resource_t
4093  find_resource(sa_group_t group, char *resname)
4094  {
4095  	sa_share_t share;
4096  	sa_resource_t resource = NULL;
4097  	char *name;
4098  
4099  	/* Iterate over all the shares and resources in the group. */
4100  	for (share = sa_get_share(group, NULL);
4101  	    share != NULL && resource == NULL;
4102  	    share = sa_get_next_share(share)) {
4103  		for (resource = sa_get_share_resource(share, NULL);
4104  		    resource != NULL;
4105  		    resource = sa_get_next_resource(resource)) {
4106  			name = sa_get_resource_attr(resource, "name");
4107  			if (name != NULL && xmlStrcasecmp((xmlChar*)name,
4108  			    (xmlChar*)resname) == 0) {
4109  				sa_free_attr_string(name);
4110  				break;
4111  			}
4112  			if (name != NULL) {
4113  				sa_free_attr_string(name);
4114  			}
4115  		}
4116  	}
4117  	return (resource);
4118  }
4119  
4120  /*
4121   * sa_find_resource(name)
4122   *
4123   * Find the named resource in the system.
4124   */
4125  
4126  sa_resource_t
4127  sa_find_resource(sa_handle_t handle, char *name)
4128  {
4129  	sa_group_t group;
4130  	sa_group_t zgroup;
4131  	sa_resource_t resource = NULL;
4132  
4133  	/*
4134  	 * Iterate over all groups and zfs subgroups and check for
4135  	 * resource name in them.
4136  	 */
4137  	for (group = sa_get_group(handle, NULL); group != NULL;
4138  	    group = sa_get_next_group(group)) {
4139  
4140  		if (is_zfs_group(group)) {
4141  			for (zgroup =
4142  			    (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
4143  			    (xmlChar *)"group");
4144  			    zgroup != NULL && resource == NULL;
4145  			    zgroup = sa_get_next_group(zgroup)) {
4146  				resource = find_resource(zgroup, name);
4147  			}
4148  		} else {
4149  			resource = find_resource(group, name);
4150  		}
4151  		if (resource != NULL)
4152  			break;
4153  	}
4154  	return (resource);
4155  }
4156  
4157  /*
4158   * sa_get_resource(group, resource)
4159   *
4160   * Search all the shares in the specified group for a share with a
4161   * resource name matching the one specified.
4162   *
4163   * In the future, it may be advantageous to allow group to be NULL and
4164   * search all groups but that isn't needed at present.
4165   */
4166  
4167  sa_resource_t
4168  sa_get_resource(sa_group_t group, char *resource)
4169  {
4170  	sa_share_t share = NULL;
4171  	sa_resource_t res = NULL;
4172  
4173  	if (resource != NULL) {
4174  		for (share = sa_get_share(group, NULL);
4175  		    share != NULL && res == NULL;
4176  		    share = sa_get_next_share(share)) {
4177  			res = sa_get_share_resource(share, resource);
4178  		}
4179  	}
4180  	return (res);
4181  }
4182  
4183  /*
4184   * get_protocol_list(optionset, object)
4185   *
4186   * Get the protocol optionset list for the object and add them as
4187   * properties to optionset.
4188   */
4189  static int
4190  get_protocol_list(sa_optionset_t optionset, void *object)
4191  {
4192  	sa_property_t prop;
4193  	sa_optionset_t opts;
4194  	int ret = SA_OK;
4195  
4196  	for (opts = sa_get_optionset(object, NULL);
4197  	    opts != NULL;
4198  	    opts = sa_get_next_optionset(opts)) {
4199  		char *type;
4200  		type = sa_get_optionset_attr(opts, "type");
4201  		/*
4202  		 * It is possible to have a non-protocol optionset. We
4203  		 * skip any of those found.
4204  		 */
4205  		if (type == NULL)
4206  			continue;
4207  		prop = sa_create_property(type, "true");
4208  		sa_free_attr_string(type);
4209  		if (prop != NULL)
4210  			prop = (sa_property_t)xmlAddChild((xmlNodePtr)optionset,
4211  			    (xmlNodePtr)prop);
4212  		/* If prop is NULL, don't bother continuing */
4213  		if (prop == NULL) {
4214  			ret = SA_NO_MEMORY;
4215  			break;
4216  		}
4217  	}
4218  	return (ret);
4219  }
4220  
4221  /*
4222   * sa_free_protoset(optionset)
4223   *
4224   * Free the protocol property optionset.
4225   */
4226  static void
4227  sa_free_protoset(sa_optionset_t optionset)
4228  {
4229  	if (optionset != NULL) {
4230  		xmlUnlinkNode((xmlNodePtr) optionset);
4231  		xmlFreeNode((xmlNodePtr) optionset);
4232  	}
4233  }
4234  
4235  /*
4236   * sa_optionset_t sa_get_active_protocols(object)
4237   *
4238   * Return a list of the protocols that are active for the object.
4239   * This is currently an internal helper function, but could be
4240   * made visible if there is enough demand for it.
4241   *
4242   * The function finds the parent group and extracts the protocol
4243   * optionsets creating a new optionset with the protocols as properties.
4244   *
4245   * The caller must free the returned optionset.
4246   */
4247  
4248  static sa_optionset_t
4249  sa_get_active_protocols(void *object)
4250  {
4251  	sa_optionset_t options;
4252  	sa_share_t share = NULL;
4253  	sa_group_t group = NULL;
4254  	sa_resource_t resource = NULL;
4255  	int ret = SA_OK;
4256  
4257  	if (object == NULL)
4258  		return (NULL);
4259  	options = (sa_optionset_t)xmlNewNode(NULL, (xmlChar *)"optionset");
4260  	if (options == NULL)
4261  		return (NULL);
4262  
4263  	/*
4264  	 * Find the objects up the tree that might have protocols
4265  	 * enabled on them.
4266  	 */
4267  	if (sa_is_resource(object)) {
4268  		resource = (sa_resource_t)object;
4269  		share = sa_get_resource_parent(resource);
4270  		group = sa_get_parent_group(share);
4271  	} else if (sa_is_share(object)) {
4272  		share = (sa_share_t)object;
4273  		group = sa_get_parent_group(share);
4274  	} else {
4275  		group = (sa_group_t)group;
4276  	}
4277  	if (resource != NULL)
4278  		ret = get_protocol_list(options, resource);
4279  	if (ret == SA_OK && share != NULL)
4280  		ret = get_protocol_list(options, share);
4281  	if (ret == SA_OK && group != NULL)
4282  		ret = get_protocol_list(options, group);
4283  
4284  	/*
4285  	 * If there was an error, we won't have a complete list so
4286  	 * abandon everything.  The caller will have to deal with the
4287  	 * issue.
4288  	 */
4289  	if (ret != SA_OK) {
4290  		sa_free_protoset(options);
4291  		options = NULL;
4292  	}
4293  	return (options);
4294  }
4295  
4296  /*
4297   * sa_enable_resource, protocol)
4298   *	Disable the specified share to the specified protocol.
4299   *	If protocol is NULL, then all protocols.
4300   */
4301  int
4302  sa_enable_resource(sa_resource_t resource, char *protocol)
4303  {
4304  	int ret = SA_OK;
4305  
4306  	if (protocol != NULL) {
4307  		ret = sa_proto_share_resource(protocol, resource);
4308  	} else {
4309  		sa_optionset_t protoset;
4310  		sa_property_t prop;
4311  		char *proto;
4312  		int err;
4313  
4314  		/* need to do all protocols */
4315  		protoset = sa_get_active_protocols(resource);
4316  		if (protoset == NULL)
4317  			return (SA_NO_MEMORY);
4318  		for (prop = sa_get_property(protoset, NULL);
4319  		    prop != NULL;
4320  		    prop = sa_get_next_property(prop)) {
4321  			proto = sa_get_property_attr(prop, "type");
4322  			if (proto == NULL) {
4323  				ret = SA_NO_MEMORY;
4324  				continue;
4325  			}
4326  			err = sa_proto_share_resource(proto, resource);
4327  			if (err != SA_OK)
4328  				ret = err;
4329  			sa_free_attr_string(proto);
4330  		}
4331  		sa_free_protoset(protoset);
4332  	}
4333  	if (ret == SA_OK)
4334  		(void) sa_set_resource_attr(resource, "shared", NULL);
4335  
4336  	return (ret);
4337  }
4338  
4339  /*
4340   * sa_disable_resource(resource, protocol)
4341   *
4342   *	Disable the specified share for the specified protocol.  If
4343   *	protocol is NULL, then all protocols.  If the underlying
4344   *	protocol doesn't implement disable at the resource level, we
4345   *	disable at the share level.
4346   */
4347  int
4348  sa_disable_resource(sa_resource_t resource, char *protocol)
4349  {
4350  	int ret = SA_OK;
4351  
4352  	if (protocol != NULL) {
4353  		ret = sa_proto_unshare_resource(protocol, resource);
4354  		if (ret == SA_NOT_IMPLEMENTED) {
4355  			sa_share_t parent;
4356  			/*
4357  			 * The protocol doesn't implement unshare
4358  			 * resource. That implies that resource names are
4359  			 * simple aliases for this protocol so we need to
4360  			 * unshare the share.
4361  			 */
4362  			parent = sa_get_resource_parent(resource);
4363  			if (parent != NULL)
4364  				ret = sa_disable_share(parent, protocol);
4365  			else
4366  				ret = SA_CONFIG_ERR;
4367  		}
4368  	} else {
4369  		sa_optionset_t protoset;
4370  		sa_property_t prop;
4371  		char *proto;
4372  		int err;
4373  
4374  		/* need to do all protocols */
4375  		protoset = sa_get_active_protocols(resource);
4376  		if (protoset == NULL)
4377  			return (SA_NO_MEMORY);
4378  		for (prop = sa_get_property(protoset, NULL);
4379  		    prop != NULL;
4380  		    prop = sa_get_next_property(prop)) {
4381  			proto = sa_get_property_attr(prop, "type");
4382  			if (proto == NULL) {
4383  				ret = SA_NO_MEMORY;
4384  				continue;
4385  			}
4386  			err = sa_proto_unshare_resource(proto, resource);
4387  			if (err == SA_NOT_SUPPORTED) {
4388  				sa_share_t parent;
4389  				parent = sa_get_resource_parent(resource);
4390  				if (parent != NULL)
4391  					err = sa_disable_share(parent, proto);
4392  				else
4393  					err = SA_CONFIG_ERR;
4394  			}
4395  			if (err != SA_OK)
4396  				ret = err;
4397  			sa_free_attr_string(proto);
4398  		}
4399  		sa_free_protoset(protoset);
4400  	}
4401  	if (ret == SA_OK)
4402  		(void) sa_set_resource_attr(resource, "shared", NULL);
4403  
4404  	return (ret);
4405  }
4406  
4407  /*
4408   * sa_set_resource_description(resource, content)
4409   *
4410   * Set the description of share to content.
4411   */
4412  
4413  int
4414  sa_set_resource_description(sa_resource_t resource, char *content)
4415  {
4416  	xmlNodePtr node;
4417  	sa_group_t group;
4418  	sa_share_t share;
4419  	int ret = SA_OK;
4420  
4421  	for (node = ((xmlNodePtr)resource)->children;
4422  	    node != NULL;
4423  	    node = node->next) {
4424  		if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
4425  			break;
4426  		}
4427  	}
4428  
4429  	/* no existing description but want to add */
4430  	if (node == NULL && content != NULL) {
4431  		/* add a description */
4432  		node = _sa_set_share_description(resource, content);
4433  	} else if (node != NULL && content != NULL) {
4434  		/* update a description */
4435  		xmlNodeSetContent(node, (xmlChar *)content);
4436  	} else if (node != NULL && content == NULL) {
4437  		/* remove an existing description */
4438  		xmlUnlinkNode(node);
4439  		xmlFreeNode(node);
4440  	}
4441  
4442  	share = sa_get_resource_parent(resource);
4443  	group = sa_get_parent_group(share);
4444  	if (group != NULL &&
4445  	    sa_is_persistent(share) && (!sa_group_is_zfs(group))) {
4446  		sa_handle_impl_t impl_handle;
4447  		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
4448  		if (impl_handle != NULL)
4449  			ret = sa_commit_share(impl_handle->scfhandle,
4450  			    group, share);
4451  		else
4452  			ret = SA_SYSTEM_ERR;
4453  	}
4454  	return (ret);
4455  }
4456  
4457  /*
4458   * sa_get_resource_description(share)
4459   *
4460   * Return the description text for the specified share if it
4461   * exists. NULL if no description exists.
4462   */
4463  
4464  char *
4465  sa_get_resource_description(sa_resource_t resource)
4466  {
4467  	xmlChar *description = NULL;
4468  	xmlNodePtr node;
4469  
4470  	for (node = ((xmlNodePtr)resource)->children; node != NULL;
4471  	    node = node->next) {
4472  		if (xmlStrcmp(node->name, (xmlChar *)"description") == 0)
4473  			break;
4474  	}
4475  	if (node != NULL) {
4476  		description = xmlNodeGetContent(node);
4477  		fixproblemchars((char *)description);
4478  	}
4479  	return ((char *)description);
4480  }
4481