xref: /titanic_51/usr/src/lib/libshare/common/libshare.c (revision c165966d2681754edbe7637da5adf3e53b3f7132)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Share control API
31  */
32 #include <stdio.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <libxml/parser.h>
40 #include <libxml/tree.h>
41 #include "libshare.h"
42 #include "libshare_impl.h"
43 #include <libscf.h>
44 #include "scfutil.h"
45 #include <ctype.h>
46 #include <libintl.h>
47 #include <thread.h>
48 #include <synch.h>
49 
50 #if _NOT_SMF
51 #define	CONFIG_FILE	"/var/tmp/share.cfg"
52 #define	CONFIG_FILE_TMP	"/var/tmp/share.cfg.tmp"
53 #endif
54 #define	TSTAMP(tm)	(uint64_t)(((uint64_t)tm.tv_sec << 32) | \
55 					(tm.tv_nsec & 0xffffffff))
56 
57 #define	DFS_LOCK_FILE	"/etc/dfs/fstypes"
58 #define	SA_STRSIZE	256	/* max string size for names */
59 
60 /*
61  * internal data structures
62  */
63 
64 extern struct sa_proto_plugin *sap_proto_list;
65 
66 /* current SMF/SVC repository handle */
67 extern void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *);
68 extern int gettransients(sa_handle_impl_t, xmlNodePtr *);
69 extern int sa_valid_property(void *, char *, sa_property_t);
70 extern char *sa_fstype(char *);
71 extern int sa_is_share(void *);
72 extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */
73 extern int sa_group_is_zfs(sa_group_t);
74 extern int sa_path_is_zfs(char *);
75 extern int sa_zfs_set_sharenfs(sa_group_t, char *, int);
76 extern void update_legacy_config(sa_handle_t);
77 extern int issubdir(char *, char *);
78 extern int sa_zfs_init(sa_handle_impl_t);
79 extern void sa_zfs_fini(sa_handle_impl_t);
80 extern void sablocksigs(sigset_t *);
81 extern void saunblocksigs(sigset_t *);
82 
83 /*
84  * Data structures for finding/managing the document root to access
85  * handle mapping. The list isn't expected to grow very large so a
86  * simple list is acceptable. The purpose is to provide a way to start
87  * with a group or share and find the library handle needed for
88  * various operations.
89  */
90 mutex_t sa_global_lock;
91 struct doc2handle {
92 	struct doc2handle	*next;
93 	xmlNodePtr		root;
94 	sa_handle_impl_t	handle;
95 };
96 
97 /* definitions used in a couple of property functions */
98 #define	SA_PROP_OP_REMOVE	1
99 #define	SA_PROP_OP_ADD		2
100 #define	SA_PROP_OP_UPDATE	3
101 
102 static struct doc2handle *sa_global_handles = NULL;
103 
104 /* helper functions */
105 
106 /*
107  * sa_errorstr(err)
108  *
109  * convert an error value to an error string
110  */
111 
112 char *
113 sa_errorstr(int err)
114 {
115 	static char errstr[32];
116 	char *ret = NULL;
117 
118 	switch (err) {
119 	case SA_OK:
120 		ret = dgettext(TEXT_DOMAIN, "ok");
121 		break;
122 	case SA_NO_SUCH_PATH:
123 		ret = dgettext(TEXT_DOMAIN, "path doesn't exist");
124 		break;
125 	case SA_NO_MEMORY:
126 		ret = dgettext(TEXT_DOMAIN, "no memory");
127 		break;
128 	case SA_DUPLICATE_NAME:
129 		ret = dgettext(TEXT_DOMAIN, "name in use");
130 		break;
131 	case SA_BAD_PATH:
132 		ret = dgettext(TEXT_DOMAIN, "bad path");
133 		break;
134 	case SA_NO_SUCH_GROUP:
135 		ret = dgettext(TEXT_DOMAIN, "no such group");
136 		break;
137 	case SA_CONFIG_ERR:
138 		ret = dgettext(TEXT_DOMAIN, "configuration error");
139 		break;
140 	case SA_SYSTEM_ERR:
141 		ret = dgettext(TEXT_DOMAIN, "system error");
142 		break;
143 	case SA_SYNTAX_ERR:
144 		ret = dgettext(TEXT_DOMAIN, "syntax error");
145 		break;
146 	case SA_NO_PERMISSION:
147 		ret = dgettext(TEXT_DOMAIN, "no permission");
148 		break;
149 	case SA_BUSY:
150 		ret = dgettext(TEXT_DOMAIN, "busy");
151 		break;
152 	case SA_NO_SUCH_PROP:
153 		ret = dgettext(TEXT_DOMAIN, "no such property");
154 		break;
155 	case SA_INVALID_NAME:
156 		ret = dgettext(TEXT_DOMAIN, "invalid name");
157 		break;
158 	case SA_INVALID_PROTOCOL:
159 		ret = dgettext(TEXT_DOMAIN, "invalid protocol");
160 		break;
161 	case SA_NOT_ALLOWED:
162 		ret = dgettext(TEXT_DOMAIN, "operation not allowed");
163 		break;
164 	case SA_BAD_VALUE:
165 		ret = dgettext(TEXT_DOMAIN, "bad property value");
166 		break;
167 	case SA_INVALID_SECURITY:
168 		ret = dgettext(TEXT_DOMAIN, "invalid security type");
169 		break;
170 	case SA_NO_SUCH_SECURITY:
171 		ret = dgettext(TEXT_DOMAIN, "security type not found");
172 		break;
173 	case SA_VALUE_CONFLICT:
174 		ret = dgettext(TEXT_DOMAIN, "property value conflict");
175 		break;
176 	case SA_NOT_IMPLEMENTED:
177 		ret = dgettext(TEXT_DOMAIN, "not implemented");
178 		break;
179 	case SA_INVALID_PATH:
180 		ret = dgettext(TEXT_DOMAIN, "invalid path");
181 		break;
182 	case SA_NOT_SUPPORTED:
183 		ret = dgettext(TEXT_DOMAIN, "operation not supported");
184 		break;
185 	case SA_PROP_SHARE_ONLY:
186 		ret = dgettext(TEXT_DOMAIN, "property not valid for group");
187 		break;
188 	case SA_NOT_SHARED:
189 		ret = dgettext(TEXT_DOMAIN, "not shared");
190 		break;
191 	default:
192 		(void) snprintf(errstr, sizeof (errstr),
193 		    dgettext(TEXT_DOMAIN, "unknown %d"), err);
194 		ret = errstr;
195 	}
196 	return (ret);
197 }
198 
199 /*
200  * Document root to active handle mapping functions.  These are only
201  * used internally. A mutex is used to prevent access while the list
202  * is changing. In general, the list will be relatively short - one
203  * item per thread that has called sa_init().
204  */
205 
206 sa_handle_impl_t
207 get_handle_for_root(xmlNodePtr root)
208 {
209 	struct doc2handle *item;
210 
211 	(void) mutex_lock(&sa_global_lock);
212 	for (item = sa_global_handles; item != NULL; item = item->next) {
213 		if (item->root == root)
214 			break;
215 	}
216 	(void) mutex_unlock(&sa_global_lock);
217 	if (item != NULL)
218 		return (item->handle);
219 	return (NULL);
220 }
221 
222 static int
223 add_handle_for_root(xmlNodePtr root, sa_handle_impl_t handle)
224 {
225 	struct doc2handle *item;
226 	int ret = SA_NO_MEMORY;
227 
228 	item = (struct doc2handle *)calloc(sizeof (struct doc2handle), 1);
229 	if (item != NULL) {
230 		item->root = root;
231 		item->handle = handle;
232 		(void) mutex_lock(&sa_global_lock);
233 		item->next = sa_global_handles;
234 		sa_global_handles = item;
235 		(void) mutex_unlock(&sa_global_lock);
236 		ret = SA_OK;
237 	}
238 	return (ret);
239 }
240 
241 /*
242  * remove_handle_for_root(root)
243  *
244  * Walks the list of handles and removes the one for this "root" from
245  * the list. It is up to the caller to free the data.
246  */
247 
248 static void
249 remove_handle_for_root(xmlNodePtr root)
250 {
251 	struct doc2handle *item, *prev;
252 
253 	(void) mutex_lock(&sa_global_lock);
254 	for (prev = NULL, item = sa_global_handles; item != NULL;
255 	    item = item->next) {
256 		if (item->root == root) {
257 			/* first in the list */
258 			if (prev == NULL)
259 				sa_global_handles = sa_global_handles->next;
260 			else
261 				prev->next = item->next;
262 			/* Item is out of the list so free the list structure */
263 			free(item);
264 			break;
265 		}
266 		prev = item;
267 	}
268 	(void) mutex_unlock(&sa_global_lock);
269 }
270 
271 /*
272  * sa_find_group_handle(sa_group_t group)
273  *
274  * Find the sa_handle_t for the configuration associated with this
275  * group.
276  */
277 sa_handle_t
278 sa_find_group_handle(sa_group_t group)
279 {
280 	xmlNodePtr node = (xmlNodePtr)group;
281 	sa_handle_t handle;
282 
283 	while (node != NULL) {
284 		if (strcmp((char *)(node->name), "sharecfg") == 0) {
285 			/* have the root so get the handle */
286 			handle = (sa_handle_t)get_handle_for_root(node);
287 			return (handle);
288 		}
289 		node = node->parent;
290 	}
291 	return (NULL);
292 }
293 
294 /*
295  * set_legacy_timestamp(root, path, timevalue)
296  *
297  * add the current timestamp value to the configuration for use in
298  * determining when to update the legacy files.  For SMF, this
299  * property is kept in default/operation/legacy_timestamp
300  */
301 
302 static void
303 set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval)
304 {
305 	xmlNodePtr node;
306 	xmlChar *lpath = NULL;
307 	sa_handle_impl_t handle;
308 
309 	/* Have to have a handle or else we weren't initialized. */
310 	handle = get_handle_for_root(root);
311 	if (handle == NULL)
312 		return;
313 
314 	for (node = root->xmlChildrenNode; node != NULL;
315 	    node = node->next) {
316 		if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) {
317 			/* a possible legacy node for this path */
318 			lpath = xmlGetProp(node, (xmlChar *)"path");
319 			if (lpath != NULL &&
320 			    xmlStrcmp(lpath, (xmlChar *)path) == 0) {
321 				xmlFree(lpath);
322 				break;
323 			}
324 			if (lpath != NULL)
325 				xmlFree(lpath);
326 		}
327 	}
328 	if (node == NULL) {
329 		/* need to create the first legacy timestamp node */
330 		node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL);
331 	}
332 	if (node != NULL) {
333 		char tstring[32];
334 		int ret;
335 
336 		(void) snprintf(tstring, sizeof (tstring), "%lld", tval);
337 		xmlSetProp(node, (xmlChar *)"timestamp", (xmlChar *)tstring);
338 		xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path);
339 		/* now commit to SMF */
340 		ret = sa_get_instance(handle->scfhandle, "default");
341 		if (ret == SA_OK) {
342 			ret = sa_start_transaction(handle->scfhandle,
343 			    "operation");
344 			if (ret == SA_OK) {
345 				ret = sa_set_property(handle->scfhandle,
346 				    "legacy-timestamp", tstring);
347 				if (ret == SA_OK) {
348 					(void) sa_end_transaction(
349 					    handle->scfhandle);
350 				} else {
351 					sa_abort_transaction(handle->scfhandle);
352 				}
353 			}
354 		}
355 	}
356 }
357 
358 /*
359  * is_shared(share)
360  *
361  * determine if the specified share is currently shared or not.
362  */
363 static int
364 is_shared(sa_share_t share)
365 {
366 	char *shared;
367 	int result = 0; /* assume not */
368 
369 	shared = sa_get_share_attr(share, "shared");
370 	if (shared != NULL) {
371 		if (strcmp(shared, "true") == 0)
372 			result = 1;
373 		sa_free_attr_string(shared);
374 	}
375 	return (result);
376 }
377 
378 /*
379  * checksubdirgroup(group, newpath, strictness)
380  *
381  * check all the specified newpath against all the paths in the
382  * group. This is a helper function for checksubdir to make it easier
383  * to also check ZFS subgroups.
384  * The strictness values mean:
385  * SA_CHECK_NORMAL == only check newpath against shares that are active
386  * SA_CHECK_STRICT == check newpath against both active shares and those
387  *		      stored in the repository
388  */
389 static int
390 checksubdirgroup(sa_group_t group, char *newpath, int strictness)
391 {
392 	sa_share_t share;
393 	char *path;
394 	int issub = SA_OK;
395 
396 	for (share = sa_get_share(group, NULL); share != NULL;
397 	    share = sa_get_next_share(share)) {
398 		/*
399 		 * The original behavior of share never checked
400 		 * against the permanent configuration
401 		 * (/etc/dfs/dfstab).  PIT has a number of cases where
402 		 * it depends on this older behavior even though it
403 		 * could be considered incorrect.  We may tighten this
404 		 * up in the future.
405 		 */
406 		if (strictness == SA_CHECK_NORMAL && !is_shared(share))
407 			continue;
408 
409 		path = sa_get_share_attr(share, "path");
410 		/*
411 		 * If path is NULL, then a share is in the process of
412 		 * construction or someone has modified the property
413 		 * group inappropriately. It should be
414 		 * ignored. issubdir() comes from the original share
415 		 * implementation and does the difficult part of
416 		 * checking subdirectories.
417 		 */
418 		if (path == NULL)
419 			continue;
420 		if (newpath != NULL &&
421 		    (strcmp(path, newpath) == 0 || issubdir(newpath, path) ||
422 		    issubdir(path, newpath))) {
423 			sa_free_attr_string(path);
424 			path = NULL;
425 			issub = SA_INVALID_PATH;
426 			break;
427 		}
428 		sa_free_attr_string(path);
429 		path = NULL;
430 	}
431 	return (issub);
432 }
433 
434 /*
435  * checksubdir(newpath, strictness)
436  *
437  * checksubdir determines if the specified path (newpath) is a
438  * subdirectory of another share. It calls checksubdirgroup() to do
439  * the complicated work. The strictness parameter determines how
440  * strict a check to make against the path. The strictness values
441  * mean: SA_CHECK_NORMAL == only check newpath against shares that are
442  * active SA_CHECK_STRICT == check newpath against both active shares
443  * and those * stored in the repository
444  */
445 static int
446 checksubdir(sa_handle_t handle, char *newpath, int strictness)
447 {
448 	sa_group_t group;
449 	int issub;
450 	char *path = NULL;
451 
452 	for (issub = 0, group = sa_get_group(handle, NULL);
453 	    group != NULL && !issub; group = sa_get_next_group(group)) {
454 		if (sa_group_is_zfs(group)) {
455 			sa_group_t subgroup;
456 			for (subgroup = sa_get_sub_group(group);
457 			    subgroup != NULL && !issub;
458 			    subgroup = sa_get_next_group(subgroup))
459 				issub = checksubdirgroup(subgroup, newpath,
460 				    strictness);
461 		} else {
462 			issub = checksubdirgroup(group, newpath, strictness);
463 		}
464 	}
465 	if (path != NULL)
466 		sa_free_attr_string(path);
467 	return (issub);
468 }
469 
470 /*
471  * validpath(path, strictness)
472  * determine if the provided path is valid for a share. It shouldn't
473  * be a sub-dir of an already shared path or the parent directory of a
474  * share path.
475  */
476 static int
477 validpath(sa_handle_t handle, char *path, int strictness)
478 {
479 	int error = SA_OK;
480 	struct stat st;
481 	sa_share_t share;
482 	char *fstype;
483 
484 	if (*path != '/')
485 		return (SA_BAD_PATH);
486 
487 	if (stat(path, &st) < 0) {
488 		error = SA_NO_SUCH_PATH;
489 	} else {
490 		share = sa_find_share(handle, path);
491 		if (share != NULL)
492 			error = SA_DUPLICATE_NAME;
493 
494 		if (error == SA_OK) {
495 			/*
496 			 * check for special case with file system
497 			 * that might have restrictions.  For now, ZFS
498 			 * is the only case since it has its own idea
499 			 * of how to configure shares. We do this
500 			 * before subdir checking since things like
501 			 * ZFS will do that for us. This should also
502 			 * be done via plugin interface.
503 			 */
504 			fstype = sa_fstype(path);
505 			if (fstype != NULL && strcmp(fstype, "zfs") == 0) {
506 				if (sa_zfs_is_shared(handle, path))
507 					error = SA_INVALID_NAME;
508 			}
509 			if (fstype != NULL)
510 				sa_free_fstype(fstype);
511 		}
512 		if (error == SA_OK)
513 			error = checksubdir(handle, path, strictness);
514 	}
515 	return (error);
516 }
517 
518 /*
519  * check to see if group/share is persistent.
520  */
521 static int
522 is_persistent(sa_group_t group)
523 {
524 	char *type;
525 	int persist = 1;
526 
527 	type = sa_get_group_attr(group, "type");
528 	if (type != NULL && strcmp(type, "transient") == 0)
529 		persist = 0;
530 	if (type != NULL)
531 		sa_free_attr_string(type);
532 	return (persist);
533 }
534 
535 /*
536  * sa_valid_group_name(name)
537  *
538  * check that the "name" contains only valid characters and otherwise
539  * fits the required naming conventions. Valid names must start with
540  * an alphabetic and the remainder may consist of only alphanumeric
541  * plus the '-' and '_' characters. This name limitation comes from
542  * inherent limitations in SMF.
543  */
544 
545 int
546 sa_valid_group_name(char *name)
547 {
548 	int ret = 1;
549 	ssize_t len;
550 
551 	if (name != NULL && isalpha(*name)) {
552 		char c;
553 		len = strlen(name);
554 		if (len < (scf_max_name_len - sizeof ("group:"))) {
555 			for (c = *name++; c != '\0' && ret != 0; c = *name++) {
556 				if (!isalnum(c) && c != '-' && c != '_')
557 					ret = 0;
558 			}
559 		} else {
560 			ret = 0;
561 		}
562 	} else {
563 		ret = 0;
564 	}
565 	return (ret);
566 }
567 
568 
569 /*
570  * is_zfs_group(group)
571  *	Determine if the specified group is a ZFS sharenfs group
572  */
573 static int
574 is_zfs_group(sa_group_t group)
575 {
576 	int ret = 0;
577 	xmlNodePtr parent;
578 	xmlChar *zfs;
579 
580 	if (strcmp((char *)((xmlNodePtr)group)->name, "share") == 0)
581 		parent = (xmlNodePtr)sa_get_parent_group(group);
582 	else
583 		parent = (xmlNodePtr)group;
584 	zfs = xmlGetProp(parent, (xmlChar *)"zfs");
585 	if (zfs != NULL) {
586 		xmlFree(zfs);
587 		ret = 1;
588 	}
589 	return (ret);
590 }
591 
592 /*
593  * sa_optionset_name(optionset, oname, len, id)
594  *	return the SMF name for the optionset. If id is not NULL, it
595  *	will have the GUID value for a share and should be used
596  *	instead of the keyword "optionset" which is used for
597  *	groups. If the optionset doesn't have a protocol type
598  *	associated with it, "default" is used. This shouldn't happen
599  *	at this point but may be desirable in the future if there are
600  *	protocol independent properties added. The name is returned in
601  *	oname.
602  */
603 
604 static int
605 sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id)
606 {
607 	char *proto;
608 
609 	if (id == NULL)
610 		id = "optionset";
611 
612 	proto = sa_get_optionset_attr(optionset, "type");
613 	len = snprintf(oname, len, "%s_%s", id, proto ? proto : "default");
614 
615 	if (proto != NULL)
616 		sa_free_attr_string(proto);
617 	return (len);
618 }
619 
620 /*
621  * sa_security_name(optionset, oname, len, id)
622  *
623  * return the SMF name for the security. If id is not NULL, it will
624  * have the GUID value for a share and should be used instead of the
625  * keyword "optionset" which is used for groups. If the optionset
626  * doesn't have a protocol type associated with it, "default" is
627  * used. This shouldn't happen at this point but may be desirable in
628  * the future if there are protocol independent properties added. The
629  * name is returned in oname. The security type is also encoded into
630  * the name. In the future, this wil *be handled a bit differently.
631  */
632 
633 static int
634 sa_security_name(sa_security_t security, char *oname, size_t len, char *id)
635 {
636 	char *proto;
637 	char *sectype;
638 
639 	if (id == NULL)
640 		id = "optionset";
641 
642 	proto = sa_get_security_attr(security, "type");
643 	sectype = sa_get_security_attr(security, "sectype");
644 	len = snprintf(oname, len, "%s_%s_%s", id, proto ? proto : "default",
645 	    sectype ? sectype : "default");
646 	if (proto != NULL)
647 		sa_free_attr_string(proto);
648 	if (sectype != NULL)
649 		sa_free_attr_string(sectype);
650 	return (len);
651 }
652 
653 /*
654  * verifydefgroupopts(handle)
655  *
656  * Make sure a "default" group exists and has default protocols enabled.
657  */
658 static void
659 verifydefgroupopts(sa_handle_t handle)
660 {
661 	sa_group_t defgrp;
662 	sa_optionset_t opt;
663 	defgrp = sa_get_group(handle, "default");
664 	if (defgrp != NULL) {
665 		opt = sa_get_optionset(defgrp, NULL);
666 		/*
667 		 * NFS is the default for default group
668 		 */
669 		if (opt == NULL)
670 			opt = sa_create_optionset(defgrp, "nfs");
671 	}
672 }
673 
674 /*
675  * sa_init(init_service)
676  *	Initialize the API
677  *	find all the shared objects
678  *	init the tables with all objects
679  *	read in the current configuration
680  */
681 
682 #define	GETPROP(prop)	scf_simple_prop_next_astring(prop)
683 #define	CHECKTSTAMP(st, tval)	stat(SA_LEGACY_DFSTAB, &st) >= 0 && \
684 	tval != TSTAMP(st.st_ctim)
685 
686 sa_handle_t
687 sa_init(int init_service)
688 {
689 	struct stat st;
690 	int legacy = 0;
691 	uint64_t tval = 0;
692 	int lockfd;
693 	sigset_t old;
694 	int updatelegacy = B_FALSE;
695 	scf_simple_prop_t *prop;
696 	sa_handle_impl_t handle;
697 	int err;
698 
699 	handle = calloc(sizeof (struct sa_handle_impl), 1);
700 
701 	if (handle != NULL) {
702 		/* get protocol specific structures */
703 		(void) proto_plugin_init();
704 		if (init_service & SA_INIT_SHARE_API) {
705 			/*
706 			 * initialize access into libzfs. We use this
707 			 * when collecting info about ZFS datasets and
708 			 * shares.
709 			 */
710 			if (sa_zfs_init(handle) == B_FALSE) {
711 				free(handle);
712 				(void) proto_plugin_fini();
713 				return (NULL);
714 			}
715 			/*
716 			 * since we want to use SMF, initialize an svc handle
717 			 * and find out what is there.
718 			 */
719 			handle->scfhandle = sa_scf_init(handle);
720 			if (handle->scfhandle != NULL) {
721 				/*
722 				 * Need to lock the extraction of the
723 				 * configuration if the dfstab file has
724 				 * changed. Lock everything now and release if
725 				 * not needed.  Use a file that isn't being
726 				 * manipulated by other parts of the system in
727 				 * order to not interfere with locking. Using
728 				 * dfstab doesn't work.
729 				 */
730 				sablocksigs(&old);
731 				lockfd = open(DFS_LOCK_FILE, O_RDWR);
732 				if (lockfd >= 0) {
733 					extern int errno;
734 					errno = 0;
735 					(void) lockf(lockfd, F_LOCK, 0);
736 					/*
737 					 * Check whether we are going to need
738 					 * to merge any dfstab changes. This
739 					 * is done by comparing the value of
740 					 * legacy-timestamp with the current
741 					 * st_ctim of the file. If they are
742 					 * different, an update is needed and
743 					 * the file must remain locked until
744 					 * the merge is done in order to
745 					 * prevent multiple startups from
746 					 * changing the SMF repository at the
747 					 * same time.  The first to get the
748 					 * lock will make any changes before
749 					 * the others can read the repository.
750 					 */
751 					prop = scf_simple_prop_get
752 					    (handle->scfhandle->handle,
753 					    (const char *)SA_SVC_FMRI_BASE
754 					    ":default", "operation",
755 					    "legacy-timestamp");
756 					if (prop != NULL) {
757 						char *i64;
758 						i64 = GETPROP(prop);
759 						if (i64 != NULL)
760 							tval = strtoull(i64,
761 							    NULL, 0);
762 						if (CHECKTSTAMP(st, tval))
763 							updatelegacy = B_TRUE;
764 						scf_simple_prop_free(prop);
765 					} else {
766 						/*
767 						 * We haven't set the
768 						 * timestamp before so do it.
769 						 */
770 						updatelegacy = B_TRUE;
771 					}
772 				}
773 				if (updatelegacy == B_FALSE) {
774 					/* Don't need the lock anymore */
775 					(void) lockf(lockfd, F_ULOCK, 0);
776 					(void) close(lockfd);
777 				}
778 
779 				/*
780 				 * It is essential that the document tree and
781 				 * the internal list of roots to handles be
782 				 * setup before anything that might try to
783 				 * create a new object is called. The document
784 				 * tree is the combination of handle->doc and
785 				 * handle->tree. This allows searches,
786 				 * etc. when all you have is an object in the
787 				 * tree.
788 				 */
789 				handle->doc = xmlNewDoc((xmlChar *)"1.0");
790 				handle->tree = xmlNewNode(NULL,
791 				    (xmlChar *)"sharecfg");
792 				if (handle->doc != NULL &&
793 				    handle->tree != NULL) {
794 					xmlDocSetRootElement(handle->doc,
795 					    handle->tree);
796 					err = add_handle_for_root(handle->tree,
797 					    handle);
798 					if (err == SA_OK)
799 						err = sa_get_config(
800 						    handle->scfhandle,
801 						    handle->tree, handle);
802 				} else {
803 					if (handle->doc != NULL)
804 						xmlFreeDoc(handle->doc);
805 					if (handle->tree != NULL)
806 						xmlFreeNode(handle->tree);
807 					err = SA_NO_MEMORY;
808 				}
809 
810 				saunblocksigs(&old);
811 
812 				if (err != SA_OK) {
813 					/*
814 					 * If we couldn't add the tree handle
815 					 * to the list, then things are going
816 					 * to fail badly. Might as well undo
817 					 * everything now and fail the
818 					 * sa_init().
819 					 */
820 					sa_fini(handle);
821 					return (NULL);
822 				}
823 
824 				if (tval == 0) {
825 					/*
826 					 * first time so make sure
827 					 * default is setup
828 					 */
829 					verifydefgroupopts(handle);
830 				}
831 
832 			if (updatelegacy == B_TRUE) {
833 				sablocksigs(&old);
834 				getlegacyconfig((sa_handle_t)handle,
835 				    SA_LEGACY_DFSTAB, &handle->tree);
836 				if (stat(SA_LEGACY_DFSTAB, &st) >= 0)
837 					set_legacy_timestamp(handle->tree,
838 					    SA_LEGACY_DFSTAB,
839 					    TSTAMP(st.st_ctim));
840 				saunblocksigs(&old);
841 				/* Safe to unlock now to allow others to run */
842 				(void) lockf(lockfd, F_ULOCK, 0);
843 				(void) close(lockfd);
844 			}
845 			legacy |= sa_get_zfs_shares(handle, "zfs");
846 			legacy |= gettransients(handle, &handle->tree);
847 		}
848 		}
849 	}
850 	return ((sa_handle_t)handle);
851 }
852 
853 /*
854  * sa_fini(handle)
855  *	Uninitialize the API structures including the configuration
856  *	data structures and ZFS related data.
857  */
858 
859 void
860 sa_fini(sa_handle_t handle)
861 {
862 	sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
863 
864 	if (impl_handle != NULL) {
865 		/*
866 		 * Free the config trees and any other data structures
867 		 * used in the handle.
868 		 */
869 		if (impl_handle->doc != NULL)
870 			xmlFreeDoc(impl_handle->doc);
871 		sa_scf_fini(impl_handle->scfhandle);
872 		sa_zfs_fini(impl_handle);
873 
874 		/* Remove and free the entry in the global list. */
875 		remove_handle_for_root(impl_handle->tree);
876 
877 		/* Make sure we free the handle */
878 		free(impl_handle);
879 
880 		/*
881 		 * If this was the last handle to release, unload the
882 		 * plugins that were loaded.
883 		 */
884 		if (sa_global_handles == NULL)
885 			(void) proto_plugin_fini();
886 
887 	}
888 }
889 
890 /*
891  * sa_get_protocols(char **protocol)
892  *	Get array of protocols that are supported
893  *	Returns pointer to an allocated and NULL terminated
894  *	array of strings.  Caller must free.
895  *	This really should be determined dynamically.
896  *	If there aren't any defined, return -1.
897  *	Use free() to return memory.
898  */
899 
900 int
901 sa_get_protocols(char ***protocols)
902 {
903 	int numproto = -1;
904 
905 	if (protocols != NULL) {
906 		struct sa_proto_plugin *plug;
907 		for (numproto = 0, plug = sap_proto_list; plug != NULL;
908 		    plug = plug->plugin_next) {
909 			numproto++;
910 		}
911 
912 		*protocols = calloc(numproto + 1,  sizeof (char *));
913 		if (*protocols != NULL) {
914 			int ret = 0;
915 			for (plug = sap_proto_list; plug != NULL;
916 			    plug = plug->plugin_next) {
917 				/* faking for now */
918 				(*protocols)[ret++] =
919 				    plug->plugin_ops->sa_protocol;
920 			}
921 		} else {
922 			numproto = -1;
923 		}
924 	}
925 	return (numproto);
926 }
927 
928 /*
929  * find_group_by_name(node, group)
930  *
931  * search the XML document subtree specified by node to find the group
932  * specified by group. Searching subtree allows subgroups to be
933  * searched for.
934  */
935 
936 static xmlNodePtr
937 find_group_by_name(xmlNodePtr node, xmlChar *group)
938 {
939 	xmlChar *name = NULL;
940 
941 	for (node = node->xmlChildrenNode; node != NULL;
942 	    node = node->next) {
943 		if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) {
944 			/* if no groupname, return the first found */
945 			if (group == NULL)
946 				break;
947 			name = xmlGetProp(node, (xmlChar *)"name");
948 			if (name != NULL && xmlStrcmp(name, group) == 0)
949 				break;
950 			if (name != NULL) {
951 				xmlFree(name);
952 				name = NULL;
953 			}
954 		}
955 	}
956 	if (name != NULL)
957 		xmlFree(name);
958 	return (node);
959 }
960 
961 /*
962  * sa_get_group(groupname)
963  *	Return the "group" specified.  If groupname is NULL,
964  *	return the first group of the list of groups.
965  */
966 sa_group_t
967 sa_get_group(sa_handle_t handle, char *groupname)
968 {
969 	xmlNodePtr node = NULL;
970 	char *subgroup = NULL;
971 	char *group = NULL;
972 	sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
973 
974 	if (impl_handle != NULL && impl_handle->tree != NULL) {
975 		if (groupname != NULL) {
976 			group = strdup(groupname);
977 			subgroup = strchr(group, '/');
978 			if (subgroup != NULL)
979 				*subgroup++ = '\0';
980 		}
981 		node = find_group_by_name(impl_handle->tree, (xmlChar *)group);
982 		/* if a subgroup, find it before returning */
983 		if (subgroup != NULL && node != NULL)
984 			node = find_group_by_name(node, (xmlChar *)subgroup);
985 	}
986 	if (node != NULL && (char *)group != NULL)
987 		(void) sa_get_instance(impl_handle->scfhandle, (char *)group);
988 	if (group != NULL)
989 		free(group);
990 	return ((sa_group_t)(node));
991 }
992 
993 /*
994  * sa_get_next_group(group)
995  *	Return the "next" group after the specified group from
996  *	the internal group list.  NULL if there are no more.
997  */
998 sa_group_t
999 sa_get_next_group(sa_group_t group)
1000 {
1001 	xmlNodePtr ngroup = NULL;
1002 	if (group != NULL) {
1003 		for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL;
1004 		    ngroup = ngroup->next) {
1005 			if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0)
1006 				break;
1007 		}
1008 	}
1009 	return ((sa_group_t)ngroup);
1010 }
1011 
1012 /*
1013  * sa_get_share(group, sharepath)
1014  *	Return the share object for the share specified. The share
1015  *	must be in the specified group.  Return NULL if not found.
1016  */
1017 sa_share_t
1018 sa_get_share(sa_group_t group, char *sharepath)
1019 {
1020 	xmlNodePtr node = NULL;
1021 	xmlChar *path;
1022 
1023 	/*
1024 	 * For future scalability, this should end up building a cache
1025 	 * since it will get called regularly by the mountd and info
1026 	 * services.
1027 	 */
1028 	if (group != NULL) {
1029 		for (node = ((xmlNodePtr)group)->children; node != NULL;
1030 		    node = node->next) {
1031 			if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1032 				if (sharepath == NULL) {
1033 					break;
1034 				} else {
1035 					/* is it the correct share? */
1036 					path = xmlGetProp(node,
1037 					    (xmlChar *)"path");
1038 					if (path != NULL &&
1039 					    xmlStrcmp(path,
1040 					    (xmlChar *)sharepath) == 0) {
1041 						xmlFree(path);
1042 						break;
1043 					}
1044 					xmlFree(path);
1045 				}
1046 			}
1047 		}
1048 	}
1049 	return ((sa_share_t)node);
1050 }
1051 
1052 /*
1053  * sa_get_next_share(share)
1054  *	Return the next share following the specified share
1055  *	from the internal list of shares. Returns NULL if there
1056  *	are no more shares.  The list is relative to the same
1057  *	group.
1058  */
1059 sa_share_t
1060 sa_get_next_share(sa_share_t share)
1061 {
1062 	xmlNodePtr node = NULL;
1063 
1064 	if (share != NULL) {
1065 		for (node = ((xmlNodePtr)share)->next; node != NULL;
1066 		    node = node->next) {
1067 			if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1068 				break;
1069 			}
1070 		}
1071 	}
1072 	return ((sa_share_t)node);
1073 }
1074 
1075 /*
1076  * _sa_get_child_node(node, type)
1077  *
1078  * find the child node of the specified node that has "type". This is
1079  * used to implement several internal functions.
1080  */
1081 
1082 static xmlNodePtr
1083 _sa_get_child_node(xmlNodePtr node, xmlChar *type)
1084 {
1085 	xmlNodePtr child;
1086 	for (child = node->xmlChildrenNode; child != NULL;
1087 	    child = child->next)
1088 		if (xmlStrcmp(child->name, type) == 0)
1089 			return (child);
1090 	return ((xmlNodePtr)NULL);
1091 }
1092 
1093 /*
1094  *  find_share(group, path)
1095  *
1096  * Search all the shares in the specified group for one that has the
1097  * specified path.
1098  */
1099 
1100 static sa_share_t
1101 find_share(sa_group_t group, char *sharepath)
1102 {
1103 	sa_share_t share;
1104 	char *path;
1105 
1106 	for (share = sa_get_share(group, NULL); share != NULL;
1107 	    share = sa_get_next_share(share)) {
1108 		path = sa_get_share_attr(share, "path");
1109 		if (path != NULL && strcmp(path, sharepath) == 0) {
1110 			sa_free_attr_string(path);
1111 			break;
1112 		}
1113 		if (path != NULL)
1114 			sa_free_attr_string(path);
1115 	}
1116 	return (share);
1117 }
1118 
1119 /*
1120  * sa_get_sub_group(group)
1121  *
1122  * Get the first sub-group of group. The sa_get_next_group() function
1123  * can be used to get the rest. This is currently only used for ZFS
1124  * sub-groups but could be used to implement a more general mechanism.
1125  */
1126 
1127 sa_group_t
1128 sa_get_sub_group(sa_group_t group)
1129 {
1130 	return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1131 	    (xmlChar *)"group"));
1132 }
1133 
1134 /*
1135  * sa_find_share(sharepath)
1136  *	Finds a share regardless of group.  In the future, this
1137  *	function should utilize a cache and hash table of some kind.
1138  *	The current assumption is that a path will only be shared
1139  *	once.  In the future, this may change as implementation of
1140  *	resource names comes into being.
1141  */
1142 sa_share_t
1143 sa_find_share(sa_handle_t handle, char *sharepath)
1144 {
1145 	sa_group_t group;
1146 	sa_group_t zgroup;
1147 	sa_share_t share = NULL;
1148 	int done = 0;
1149 
1150 	for (group = sa_get_group(handle, NULL); group != NULL && !done;
1151 	    group = sa_get_next_group(group)) {
1152 		if (is_zfs_group(group)) {
1153 			for (zgroup =
1154 			    (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1155 			    (xmlChar *)"group");
1156 			    zgroup != NULL;
1157 			    zgroup = sa_get_next_group(zgroup)) {
1158 				share = find_share(zgroup, sharepath);
1159 				if (share != NULL)
1160 					break;
1161 			}
1162 		} else {
1163 			share = find_share(group, sharepath);
1164 		}
1165 		if (share != NULL)
1166 			break;
1167 	}
1168 	return (share);
1169 }
1170 
1171 /*
1172  *  sa_check_path(group, path, strictness)
1173  *
1174  * check that path is a valid path relative to the group.  Currently,
1175  * we are ignoring the group and checking only the NFS rules. Later,
1176  * we may want to use the group to then check against the protocols
1177  * enabled on the group. The strictness values mean:
1178  * SA_CHECK_NORMAL == only check newpath against shares that are active
1179  * SA_CHECK_STRICT == check newpath against both active shares and those
1180  *		      stored in the repository
1181  */
1182 
1183 int
1184 sa_check_path(sa_group_t group, char *path, int strictness)
1185 {
1186 	sa_handle_t handle;
1187 
1188 	handle = sa_find_group_handle(group);
1189 	return (validpath(handle, path, strictness));
1190 }
1191 
1192 /*
1193  * _sa_add_share(group, sharepath, persist, *error)
1194  *
1195  * common code for all types of add_share. sa_add_share() is the
1196  * public API, we also need to be able to do this when parsing legacy
1197  * files and construction of the internal configuration while
1198  * extracting config info from SMF.
1199  */
1200 
1201 sa_share_t
1202 _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
1203 {
1204 	xmlNodePtr node = NULL;
1205 	int err;
1206 
1207 	err  = SA_OK; /* assume success */
1208 
1209 	node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"share", NULL);
1210 	if (node != NULL) {
1211 		xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath);
1212 		xmlSetProp(node, (xmlChar *)"type",
1213 		    persist ? (xmlChar *)"persist" : (xmlChar *)"transient");
1214 		if (persist != SA_SHARE_TRANSIENT) {
1215 			/*
1216 			 * persistent shares come in two flavors: SMF and
1217 			 * ZFS. Sort this one out based on target group and
1218 			 * path type. Currently, only NFS is supported in the
1219 			 * ZFS group and it is always on.
1220 			 */
1221 			if (sa_group_is_zfs(group) &&
1222 			    sa_path_is_zfs(sharepath)) {
1223 				err = sa_zfs_set_sharenfs(group, sharepath, 1);
1224 			} else {
1225 				sa_handle_impl_t impl_handle;
1226 				impl_handle =
1227 				    (sa_handle_impl_t)sa_find_group_handle(
1228 				    group);
1229 				if (impl_handle != NULL) {
1230 					err = sa_commit_share(
1231 					    impl_handle->scfhandle, group,
1232 					    (sa_share_t)node);
1233 				} else {
1234 					err = SA_SYSTEM_ERR;
1235 				}
1236 			}
1237 		}
1238 		if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) {
1239 			/* called by the dfstab parser so could be a show */
1240 			err = SA_OK;
1241 		}
1242 		if (err != SA_OK) {
1243 			/*
1244 			 * we couldn't commit to the repository so undo
1245 			 * our internal state to reflect reality.
1246 			 */
1247 			xmlUnlinkNode(node);
1248 			xmlFreeNode(node);
1249 			node = NULL;
1250 		}
1251 	} else {
1252 		err = SA_NO_MEMORY;
1253 	}
1254 	if (error != NULL)
1255 		*error = err;
1256 	return (node);
1257 }
1258 
1259 /*
1260  * sa_add_share(group, sharepath, persist, *error)
1261  *
1262  *	Add a new share object to the specified group.  The share will
1263  *	have the specified sharepath and will only be constructed if
1264  *	it is a valid path to be shared.  NULL is returned on error
1265  *	and a detailed error value will be returned via the error
1266  *	pointer.
1267  */
1268 sa_share_t
1269 sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
1270 {
1271 	xmlNodePtr node = NULL;
1272 	sa_share_t dup;
1273 	int strictness = SA_CHECK_NORMAL;
1274 	sa_handle_t handle;
1275 
1276 	/*
1277 	 * If the share is to be permanent, use strict checking so a
1278 	 * bad config doesn't get created. Transient shares only need
1279 	 * to check against the currently active
1280 	 * shares. SA_SHARE_PARSER is a modifier used internally to
1281 	 * indicate that we are being called by the dfstab parser and
1282 	 * that we need strict checking in all cases. Normally persist
1283 	 * is in integer value but SA_SHARE_PARSER may be or'd into
1284 	 * it as an override.
1285 	 */
1286 	if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT)
1287 		strictness = SA_CHECK_STRICT;
1288 
1289 	handle = sa_find_group_handle(group);
1290 
1291 	if ((dup = sa_find_share(handle, sharepath)) == NULL &&
1292 	    (*error = sa_check_path(group, sharepath, strictness)) == SA_OK) {
1293 		node = _sa_add_share(group, sharepath, persist, error);
1294 	}
1295 	if (dup != NULL)
1296 		*error = SA_DUPLICATE_NAME;
1297 
1298 	return ((sa_share_t)node);
1299 }
1300 
1301 /*
1302  * sa_enable_share(share, protocol)
1303  *	Enable the specified share to the specified protocol.
1304  *	If protocol is NULL, then all protocols.
1305  */
1306 int
1307 sa_enable_share(sa_share_t share, char *protocol)
1308 {
1309 	char *sharepath;
1310 	struct stat st;
1311 	int err = 0;
1312 
1313 	sharepath = sa_get_share_attr(share, "path");
1314 	if (stat(sharepath, &st) < 0) {
1315 		err = SA_NO_SUCH_PATH;
1316 	} else {
1317 		/* tell the server about the share */
1318 		if (protocol != NULL) {
1319 			/* lookup protocol specific handler */
1320 			err = sa_proto_share(protocol, share);
1321 			if (err == SA_OK)
1322 				(void) sa_set_share_attr(share, "shared",
1323 				    "true");
1324 		} else {
1325 			/*
1326 			 * Tell all protocols.  Only NFS for now but
1327 			 * SMB is coming.
1328 			 */
1329 			err = sa_proto_share("nfs", share);
1330 			(void) sa_set_share_attr(share, "shared", "true");
1331 		}
1332 	}
1333 	if (sharepath != NULL)
1334 		sa_free_attr_string(sharepath);
1335 	return (err);
1336 }
1337 
1338 /*
1339  * sa_disable_share(share, protocol)
1340  *	Disable the specified share to the specified protocol.
1341  *	If protocol is NULL, then all protocols.
1342  */
1343 int
1344 sa_disable_share(sa_share_t share, char *protocol)
1345 {
1346 	char *path;
1347 	char *shared;
1348 	int ret = SA_OK;
1349 
1350 	path = sa_get_share_attr(share, "path");
1351 	shared = sa_get_share_attr(share, "shared");
1352 
1353 	if (protocol != NULL) {
1354 		ret = sa_proto_unshare(protocol, path);
1355 	} else {
1356 		/* need to do all protocols */
1357 		ret = sa_proto_unshare("nfs", path);
1358 	}
1359 	if (ret == SA_OK)
1360 		(void) sa_set_share_attr(share, "shared", NULL);
1361 	if (path != NULL)
1362 		sa_free_attr_string(path);
1363 	if (shared != NULL)
1364 		sa_free_attr_string(shared);
1365 	return (ret);
1366 }
1367 
1368 /*
1369  * sa_remove_share(share)
1370  *
1371  * remove the specified share from its containing group.
1372  * Remove from the SMF or ZFS configuration space.
1373  */
1374 
1375 int
1376 sa_remove_share(sa_share_t share)
1377 {
1378 	sa_group_t group;
1379 	int ret = SA_OK;
1380 	char *type;
1381 	int transient = 0;
1382 	char *groupname;
1383 	char *zfs;
1384 
1385 	type = sa_get_share_attr(share, "type");
1386 	group = sa_get_parent_group(share);
1387 	zfs = sa_get_group_attr(group, "zfs");
1388 	groupname = sa_get_group_attr(group, "name");
1389 	if (type != NULL && strcmp(type, "persist") != 0)
1390 		transient = 1;
1391 	if (type != NULL)
1392 		sa_free_attr_string(type);
1393 
1394 	/* remove the node from its group then free the memory */
1395 
1396 	/*
1397 	 * need to test if "busy"
1398 	 */
1399 	/* only do SMF action if permanent */
1400 	if (!transient || zfs != NULL) {
1401 		/* remove from legacy dfstab as well as possible SMF */
1402 		ret = sa_delete_legacy(share);
1403 		if (ret == SA_OK) {
1404 			if (!sa_group_is_zfs(group)) {
1405 				sa_handle_impl_t impl_handle;
1406 				impl_handle = (sa_handle_impl_t)
1407 				    sa_find_group_handle(group);
1408 				if (impl_handle != NULL) {
1409 					ret = sa_delete_share(
1410 					    impl_handle->scfhandle, group,
1411 					    share);
1412 				} else {
1413 					ret = SA_SYSTEM_ERR;
1414 				}
1415 			} else {
1416 				char *sharepath = sa_get_share_attr(share,
1417 				    "path");
1418 				if (sharepath != NULL) {
1419 					ret = sa_zfs_set_sharenfs(group,
1420 					    sharepath, 0);
1421 					sa_free_attr_string(sharepath);
1422 				}
1423 			}
1424 		}
1425 	}
1426 	if (groupname != NULL)
1427 		sa_free_attr_string(groupname);
1428 	if (zfs != NULL)
1429 		sa_free_attr_string(zfs);
1430 
1431 	xmlUnlinkNode((xmlNodePtr)share);
1432 	xmlFreeNode((xmlNodePtr)share);
1433 	return (ret);
1434 }
1435 
1436 /*
1437  * sa_move_share(group, share)
1438  *
1439  * move the specified share to the specified group.  Update SMF
1440  * appropriately.
1441  */
1442 
1443 int
1444 sa_move_share(sa_group_t group, sa_share_t share)
1445 {
1446 	sa_group_t oldgroup;
1447 	int ret = SA_OK;
1448 
1449 	/* remove the node from its group then free the memory */
1450 
1451 	oldgroup = sa_get_parent_group(share);
1452 	if (oldgroup != group) {
1453 		sa_handle_impl_t impl_handle;
1454 		xmlUnlinkNode((xmlNodePtr)share);
1455 		/*
1456 		 * now that the share isn't in its old group, add to
1457 		 * the new one
1458 		 */
1459 		xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share);
1460 		/* need to deal with SMF */
1461 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1462 		if (impl_handle != NULL) {
1463 			/*
1464 			 * need to remove from old group first and then add to
1465 			 * new group. Ideally, we would do the other order but
1466 			 * need to avoid having the share in two groups at the
1467 			 * same time.
1468 			 */
1469 			ret = sa_delete_share(impl_handle->scfhandle, oldgroup,
1470 			    share);
1471 			if (ret == SA_OK)
1472 				ret = sa_commit_share(impl_handle->scfhandle,
1473 				    group, share);
1474 		} else {
1475 			ret = SA_SYSTEM_ERR;
1476 		}
1477 	}
1478 	return (ret);
1479 }
1480 
1481 /*
1482  * sa_get_parent_group(share)
1483  *
1484  * Return the containg group for the share. If a group was actually
1485  * passed in, we don't want a parent so return NULL.
1486  */
1487 
1488 sa_group_t
1489 sa_get_parent_group(sa_share_t share)
1490 {
1491 	xmlNodePtr node = NULL;
1492 	if (share != NULL) {
1493 		node = ((xmlNodePtr)share)->parent;
1494 		/*
1495 		 * make sure parent is a group and not sharecfg since
1496 		 * we may be cheating and passing in a group.
1497 		 * Eventually, groups of groups might come into being.
1498 		 */
1499 		if (node == NULL ||
1500 		    xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0)
1501 			node = NULL;
1502 	}
1503 	return ((sa_group_t)node);
1504 }
1505 
1506 /*
1507  * _sa_create_group(impl_handle, groupname)
1508  *
1509  * Create a group in the document. The caller will need to deal with
1510  * configuration store and activation.
1511  */
1512 
1513 sa_group_t
1514 _sa_create_group(sa_handle_impl_t impl_handle, char *groupname)
1515 {
1516 	xmlNodePtr node = NULL;
1517 
1518 	if (sa_valid_group_name(groupname)) {
1519 		node = xmlNewChild(impl_handle->tree, NULL, (xmlChar *)"group",
1520 		    NULL);
1521 		if (node != NULL) {
1522 			xmlSetProp(node, (xmlChar *)"name",
1523 			    (xmlChar *)groupname);
1524 			xmlSetProp(node, (xmlChar *)"state",
1525 			    (xmlChar *)"enabled");
1526 		}
1527 	}
1528 	return ((sa_group_t)node);
1529 }
1530 
1531 /*
1532  * _sa_create_zfs_group(group, groupname)
1533  *
1534  * Create a ZFS subgroup under the specified group. This may
1535  * eventually form the basis of general sub-groups, but is currently
1536  * restricted to ZFS.
1537  */
1538 sa_group_t
1539 _sa_create_zfs_group(sa_group_t group, char *groupname)
1540 {
1541 	xmlNodePtr node = NULL;
1542 
1543 	node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"group", NULL);
1544 	if (node != NULL) {
1545 		xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname);
1546 		xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled");
1547 	}
1548 
1549 	return ((sa_group_t)node);
1550 }
1551 
1552 /*
1553  * sa_create_group(groupname, *error)
1554  *
1555  * Create a new group with groupname.  Need to validate that it is a
1556  * legal name for SMF and the construct the SMF service instance of
1557  * svc:/network/shares/group to implement the group. All necessary
1558  * operational properties must be added to the group at this point
1559  * (via the SMF transaction model).
1560  */
1561 sa_group_t
1562 sa_create_group(sa_handle_t handle, char *groupname, int *error)
1563 {
1564 	xmlNodePtr node = NULL;
1565 	sa_group_t group;
1566 	int ret;
1567 	char rbacstr[SA_STRSIZE];
1568 	sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
1569 
1570 	ret = SA_OK;
1571 
1572 	if (impl_handle == NULL || impl_handle->scfhandle == NULL) {
1573 		ret = SA_SYSTEM_ERR;
1574 		goto err;
1575 	}
1576 
1577 	group = sa_get_group(handle, groupname);
1578 	if (group != NULL) {
1579 		ret = SA_DUPLICATE_NAME;
1580 	} else {
1581 		if (sa_valid_group_name(groupname)) {
1582 			node = xmlNewChild(impl_handle->tree, NULL,
1583 			    (xmlChar *)"group", NULL);
1584 			if (node != NULL) {
1585 				xmlSetProp(node, (xmlChar *)"name",
1586 				    (xmlChar *)groupname);
1587 				/* default to the group being enabled */
1588 				xmlSetProp(node, (xmlChar *)"state",
1589 				    (xmlChar *)"enabled");
1590 				ret = sa_create_instance(impl_handle->scfhandle,
1591 				    groupname);
1592 				if (ret == SA_OK) {
1593 					ret = sa_start_transaction(
1594 					    impl_handle->scfhandle,
1595 					    "operation");
1596 				}
1597 				if (ret == SA_OK) {
1598 					ret = sa_set_property(
1599 					    impl_handle->scfhandle,
1600 					    "state", "enabled");
1601 					if (ret == SA_OK) {
1602 						ret = sa_end_transaction(
1603 						    impl_handle->scfhandle);
1604 					} else {
1605 						sa_abort_transaction(
1606 						    impl_handle->scfhandle);
1607 					}
1608 				}
1609 				if (ret == SA_OK) {
1610 					/* initialize the RBAC strings */
1611 					ret = sa_start_transaction(
1612 					    impl_handle->scfhandle,
1613 					    "general");
1614 					if (ret == SA_OK) {
1615 						(void) snprintf(rbacstr,
1616 						    sizeof (rbacstr), "%s.%s",
1617 						    SA_RBAC_MANAGE, groupname);
1618 						ret = sa_set_property(
1619 						    impl_handle->scfhandle,
1620 						    "action_authorization",
1621 						    rbacstr);
1622 					}
1623 					if (ret == SA_OK) {
1624 						(void) snprintf(rbacstr,
1625 						    sizeof (rbacstr), "%s.%s",
1626 						    SA_RBAC_VALUE, groupname);
1627 						ret = sa_set_property(
1628 						    impl_handle->scfhandle,
1629 						    "value_authorization",
1630 						    rbacstr);
1631 					}
1632 					if (ret == SA_OK) {
1633 						ret = sa_end_transaction(
1634 						    impl_handle->scfhandle);
1635 					} else {
1636 						sa_abort_transaction(
1637 						    impl_handle->scfhandle);
1638 					}
1639 				}
1640 				if (ret != SA_OK) {
1641 					/*
1642 					 * Couldn't commit the group
1643 					 * so we need to undo
1644 					 * internally.
1645 					 */
1646 					xmlUnlinkNode(node);
1647 					xmlFreeNode(node);
1648 					node = NULL;
1649 				}
1650 			} else {
1651 				ret = SA_NO_MEMORY;
1652 			}
1653 		} else {
1654 			ret = SA_INVALID_NAME;
1655 		}
1656 	}
1657 err:
1658 	if (error != NULL)
1659 		*error = ret;
1660 	return ((sa_group_t)node);
1661 }
1662 
1663 /*
1664  * sa_remove_group(group)
1665  *
1666  * Remove the specified group. This deletes from the SMF repository.
1667  * All property groups and properties are removed.
1668  */
1669 
1670 int
1671 sa_remove_group(sa_group_t group)
1672 {
1673 	char *name;
1674 	int ret = SA_OK;
1675 	sa_handle_impl_t impl_handle;
1676 
1677 	impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1678 	if (impl_handle != NULL) {
1679 		name = sa_get_group_attr(group, "name");
1680 		if (name != NULL) {
1681 			ret = sa_delete_instance(impl_handle->scfhandle, name);
1682 			sa_free_attr_string(name);
1683 		}
1684 		xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */
1685 		xmlFreeNode((xmlNodePtr)group);   /* now it is gone */
1686 	} else {
1687 		ret = SA_SYSTEM_ERR;
1688 	}
1689 	return (ret);
1690 }
1691 
1692 /*
1693  * sa_update_config()
1694  *
1695  * Used to update legacy files that need to be updated in bulk
1696  * Currently, this is a placeholder and will go away in a future
1697  * release.
1698  */
1699 
1700 int
1701 sa_update_config(sa_handle_t handle)
1702 {
1703 	/*
1704 	 * do legacy files first so we can tell when they change.
1705 	 * This will go away when we start updating individual records
1706 	 * rather than the whole file.
1707 	 */
1708 	update_legacy_config(handle);
1709 	return (SA_OK);
1710 }
1711 
1712 /*
1713  * get_node_attr(node, tag)
1714  *
1715  * Get the speficied tag(attribute) if it exists on the node.  This is
1716  * used internally by a number of attribute oriented functions.
1717  */
1718 
1719 static char *
1720 get_node_attr(void *nodehdl, char *tag)
1721 {
1722 	xmlNodePtr node = (xmlNodePtr)nodehdl;
1723 	xmlChar *name = NULL;
1724 
1725 	if (node != NULL)
1726 		name = xmlGetProp(node, (xmlChar *)tag);
1727 	return ((char *)name);
1728 }
1729 
1730 /*
1731  * get_node_attr(node, tag)
1732  *
1733  * Set the speficied tag(attribute) to the specified value This is
1734  * used internally by a number of attribute oriented functions. It
1735  * doesn't update the repository, only the internal document state.
1736  */
1737 
1738 void
1739 set_node_attr(void *nodehdl, char *tag, char *value)
1740 {
1741 	xmlNodePtr node = (xmlNodePtr)nodehdl;
1742 	if (node != NULL && tag != NULL) {
1743 		if (value != NULL)
1744 			xmlSetProp(node, (xmlChar *)tag, (xmlChar *)value);
1745 		else
1746 			xmlUnsetProp(node, (xmlChar *)tag);
1747 	}
1748 }
1749 
1750 /*
1751  * sa_get_group_attr(group, tag)
1752  *
1753  * Get the specied attribute, if defined, for the group.
1754  */
1755 
1756 char *
1757 sa_get_group_attr(sa_group_t group, char *tag)
1758 {
1759 	return (get_node_attr((void *)group, tag));
1760 }
1761 
1762 /*
1763  * sa_set_group_attr(group, tag, value)
1764  *
1765  * set the specified tag/attribute on the group using value as its
1766  * value.
1767  *
1768  * This will result in setting the property in the SMF repository as
1769  * well as in the internal document.
1770  */
1771 
1772 int
1773 sa_set_group_attr(sa_group_t group, char *tag, char *value)
1774 {
1775 	int ret;
1776 	char *groupname;
1777 	sa_handle_impl_t impl_handle;
1778 
1779 	impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1780 	if (impl_handle != NULL) {
1781 		groupname = sa_get_group_attr(group, "name");
1782 		ret = sa_get_instance(impl_handle->scfhandle, groupname);
1783 		if (ret == SA_OK) {
1784 			set_node_attr((void *)group, tag, value);
1785 			ret = sa_start_transaction(impl_handle->scfhandle,
1786 			    "operation");
1787 			if (ret == SA_OK) {
1788 				ret = sa_set_property(impl_handle->scfhandle,
1789 				    tag, value);
1790 				if (ret == SA_OK)
1791 					(void) sa_end_transaction(
1792 					    impl_handle->scfhandle);
1793 				else
1794 					sa_abort_transaction(
1795 					    impl_handle->scfhandle);
1796 			}
1797 		}
1798 		if (groupname != NULL)
1799 			sa_free_attr_string(groupname);
1800 	} else {
1801 		ret = SA_SYSTEM_ERR;
1802 	}
1803 	return (ret);
1804 }
1805 
1806 /*
1807  * sa_get_share_attr(share, tag)
1808  *
1809  * Return the value of the tag/attribute set on the specified
1810  * share. Returns NULL if the tag doesn't exist.
1811  */
1812 
1813 char *
1814 sa_get_share_attr(sa_share_t share, char *tag)
1815 {
1816 	return (get_node_attr((void *)share, tag));
1817 }
1818 
1819 /*
1820  * sa_get_resource(group, resource)
1821  *
1822  * Search all the shares in the speified group for a share with a
1823  * resource name matching the one specified.
1824  *
1825  * In the future, it may be advantageous to allow group to be NULL and
1826  * search all groups but that isn't needed at present.
1827  */
1828 
1829 sa_share_t
1830 sa_get_resource(sa_group_t group, char *resource)
1831 {
1832 	sa_share_t share = NULL;
1833 	char *name = NULL;
1834 
1835 	if (resource != NULL) {
1836 		for (share = sa_get_share(group, NULL); share != NULL;
1837 		    share = sa_get_next_share(share)) {
1838 			name = sa_get_share_attr(share, "resource");
1839 			if (name != NULL) {
1840 				if (strcmp(name, resource) == 0)
1841 					break;
1842 				sa_free_attr_string(name);
1843 				name = NULL;
1844 			}
1845 		}
1846 		if (name != NULL)
1847 			sa_free_attr_string(name);
1848 	}
1849 	return ((sa_share_t)share);
1850 }
1851 
1852 /*
1853  * _sa_set_share_description(share, description)
1854  *
1855  * Add a description tag with text contents to the specified share.
1856  * A separate XML tag is used rather than a property.
1857  */
1858 
1859 xmlNodePtr
1860 _sa_set_share_description(sa_share_t share, char *content)
1861 {
1862 	xmlNodePtr node;
1863 	node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description",
1864 	    NULL);
1865 	xmlNodeSetContent(node, (xmlChar *)content);
1866 	return (node);
1867 }
1868 
1869 /*
1870  * sa_set_share_attr(share, tag, value)
1871  *
1872  * Set the share attribute specified by tag to the specified value. In
1873  * the case of "resource", enforce a no duplicates in a group rule. If
1874  * the share is not transient, commit the changes to the repository
1875  * else just update the share internally.
1876  */
1877 
1878 int
1879 sa_set_share_attr(sa_share_t share, char *tag, char *value)
1880 {
1881 	sa_group_t group;
1882 	sa_share_t resource;
1883 	int ret = SA_OK;
1884 
1885 	group = sa_get_parent_group(share);
1886 
1887 	/*
1888 	 * There are some attributes that may have specific
1889 	 * restrictions on them. Initially, only "resource" has
1890 	 * special meaning that needs to be checked. Only one instance
1891 	 * of a resource name may exist within a group.
1892 	 */
1893 
1894 	if (strcmp(tag, "resource") == 0) {
1895 		resource = sa_get_resource(group, value);
1896 		if (resource != share && resource != NULL)
1897 			ret = SA_DUPLICATE_NAME;
1898 	}
1899 	if (ret == SA_OK) {
1900 		set_node_attr((void *)share, tag, value);
1901 		if (group != NULL) {
1902 			char *type;
1903 			/* we can probably optimize this some */
1904 			type = sa_get_share_attr(share, "type");
1905 			if (type == NULL || strcmp(type, "transient") != 0) {
1906 				sa_handle_impl_t impl_handle;
1907 				impl_handle =
1908 				    (sa_handle_impl_t)sa_find_group_handle(
1909 				    group);
1910 				if (impl_handle != NULL) {
1911 					ret = sa_commit_share(
1912 					    impl_handle->scfhandle, group,
1913 					    share);
1914 				} else {
1915 					ret = SA_SYSTEM_ERR;
1916 				}
1917 			}
1918 			if (type != NULL)
1919 				sa_free_attr_string(type);
1920 		}
1921 	}
1922 	return (ret);
1923 }
1924 
1925 /*
1926  * sa_get_property_attr(prop, tag)
1927  *
1928  * Get the value of the specified property attribute. Standard
1929  * attributes are "type" and "value".
1930  */
1931 
1932 char *
1933 sa_get_property_attr(sa_property_t prop, char *tag)
1934 {
1935 	return (get_node_attr((void *)prop, tag));
1936 }
1937 
1938 /*
1939  * sa_get_optionset_attr(prop, tag)
1940  *
1941  * Get the value of the specified property attribute. Standard
1942  * attribute is "type".
1943  */
1944 
1945 char *
1946 sa_get_optionset_attr(sa_property_t optionset, char *tag)
1947 {
1948 	return (get_node_attr((void *)optionset, tag));
1949 
1950 }
1951 
1952 /*
1953  * sa_set_optionset_attr(optionset, tag, value)
1954  *
1955  * Set the specified attribute(tag) to the specified value on the
1956  * optionset.
1957  */
1958 
1959 void
1960 sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value)
1961 {
1962 	set_node_attr((void *)optionset, tag, value);
1963 }
1964 
1965 /*
1966  * sa_free_attr_string(string)
1967  *
1968  * Free the string that was returned in one of the sa_get_*_attr()
1969  * functions.
1970  */
1971 
1972 void
1973 sa_free_attr_string(char *string)
1974 {
1975 	xmlFree((xmlChar *)string);
1976 }
1977 
1978 /*
1979  * sa_get_optionset(group, proto)
1980  *
1981  * Return the optionset, if it exists, that is associated with the
1982  * specified protocol.
1983  */
1984 
1985 sa_optionset_t
1986 sa_get_optionset(void *group, char *proto)
1987 {
1988 	xmlNodePtr node;
1989 	xmlChar *value = NULL;
1990 
1991 	for (node = ((xmlNodePtr)group)->children; node != NULL;
1992 	    node = node->next) {
1993 		if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
1994 			value = xmlGetProp(node, (xmlChar *)"type");
1995 			if (proto != NULL) {
1996 				if (value != NULL &&
1997 				    xmlStrcmp(value, (xmlChar *)proto) == 0) {
1998 					break;
1999 				}
2000 				if (value != NULL) {
2001 					xmlFree(value);
2002 					value = NULL;
2003 				}
2004 			} else {
2005 				break;
2006 			}
2007 		}
2008 	}
2009 	if (value != NULL)
2010 		xmlFree(value);
2011 	return ((sa_optionset_t)node);
2012 }
2013 
2014 /*
2015  * sa_get_next_optionset(optionset)
2016  *
2017  * Return the next optionset in the group. NULL if this was the last.
2018  */
2019 
2020 sa_optionset_t
2021 sa_get_next_optionset(sa_optionset_t optionset)
2022 {
2023 	xmlNodePtr node;
2024 
2025 	for (node = ((xmlNodePtr)optionset)->next; node != NULL;
2026 	    node = node->next) {
2027 		if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2028 			break;
2029 		}
2030 	}
2031 	return ((sa_optionset_t)node);
2032 }
2033 
2034 /*
2035  * sa_get_security(group, sectype, proto)
2036  *
2037  * Return the security optionset. The internal name is a hold over
2038  * from the implementation and will be changed before the API is
2039  * finalized. This is really a named optionset that can be negotiated
2040  * as a group of properties (like NFS security options).
2041  */
2042 
2043 sa_security_t
2044 sa_get_security(sa_group_t group, char *sectype, char *proto)
2045 {
2046 	xmlNodePtr node;
2047 	xmlChar *value = NULL;
2048 
2049 	for (node = ((xmlNodePtr)group)->children; node != NULL;
2050 	    node = node->next) {
2051 		if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2052 			if (proto != NULL) {
2053 				value = xmlGetProp(node, (xmlChar *)"type");
2054 				if (value == NULL ||
2055 				    (value != NULL &&
2056 				    xmlStrcmp(value, (xmlChar *)proto) != 0)) {
2057 					/* it doesn't match so continue */
2058 					xmlFree(value);
2059 					value = NULL;
2060 					continue;
2061 				}
2062 			}
2063 			if (value != NULL) {
2064 				xmlFree(value);
2065 				value = NULL;
2066 			}
2067 			/* potential match */
2068 			if (sectype != NULL) {
2069 				value = xmlGetProp(node, (xmlChar *)"sectype");
2070 				if (value != NULL &&
2071 				    xmlStrcmp(value, (xmlChar *)sectype) == 0) {
2072 					break;
2073 				}
2074 			} else {
2075 				break;
2076 			}
2077 		}
2078 		if (value != NULL) {
2079 			xmlFree(value);
2080 			value = NULL;
2081 		}
2082 	}
2083 	if (value != NULL)
2084 		xmlFree(value);
2085 	return ((sa_security_t)node);
2086 }
2087 
2088 /*
2089  * sa_get_next_security(security)
2090  *
2091  * Get the next security optionset if one exists.
2092  */
2093 
2094 sa_security_t
2095 sa_get_next_security(sa_security_t security)
2096 {
2097 	xmlNodePtr node;
2098 
2099 	for (node = ((xmlNodePtr)security)->next; node != NULL;
2100 	    node = node->next) {
2101 		if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2102 			break;
2103 		}
2104 	}
2105 	return ((sa_security_t)node);
2106 }
2107 
2108 /*
2109  * sa_get_property(optionset, prop)
2110  *
2111  * Get the property object with the name specified in prop from the
2112  * optionset.
2113  */
2114 
2115 sa_property_t
2116 sa_get_property(sa_optionset_t optionset, char *prop)
2117 {
2118 	xmlNodePtr node = (xmlNodePtr)optionset;
2119 	xmlChar *value = NULL;
2120 
2121 	if (optionset == NULL)
2122 		return (NULL);
2123 
2124 	for (node = node->children; node != NULL;
2125 	    node = node->next) {
2126 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2127 			if (prop == NULL)
2128 				break;
2129 			value = xmlGetProp(node, (xmlChar *)"type");
2130 			if (value != NULL &&
2131 			    xmlStrcmp(value, (xmlChar *)prop) == 0) {
2132 				break;
2133 			}
2134 			if (value != NULL) {
2135 				xmlFree(value);
2136 				value = NULL;
2137 			}
2138 		}
2139 	}
2140 	if (value != NULL)
2141 		xmlFree(value);
2142 	if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
2143 		/*
2144 		 * avoid a non option node -- it is possible to be a
2145 		 * text node
2146 		 */
2147 		node = NULL;
2148 	}
2149 	return ((sa_property_t)node);
2150 }
2151 
2152 /*
2153  * sa_get_next_property(property)
2154  *
2155  * Get the next property following the specified property. NULL if
2156  * this was the last.
2157  */
2158 
2159 sa_property_t
2160 sa_get_next_property(sa_property_t property)
2161 {
2162 	xmlNodePtr node;
2163 
2164 	for (node = ((xmlNodePtr)property)->next; node != NULL;
2165 	    node = node->next) {
2166 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2167 			break;
2168 		}
2169 	}
2170 	return ((sa_property_t)node);
2171 }
2172 
2173 /*
2174  * sa_set_share_description(share, content)
2175  *
2176  * Set the description of share to content.
2177  */
2178 
2179 int
2180 sa_set_share_description(sa_share_t share, char *content)
2181 {
2182 	xmlNodePtr node;
2183 	sa_group_t group;
2184 	int ret = SA_OK;
2185 
2186 	for (node = ((xmlNodePtr)share)->children; node != NULL;
2187 	    node = node->next) {
2188 		if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2189 			break;
2190 		}
2191 	}
2192 	group = sa_get_parent_group(share);
2193 	/* no existing description but want to add */
2194 	if (node == NULL && content != NULL) {
2195 		/* add a description */
2196 		node = _sa_set_share_description(share, content);
2197 	} else if (node != NULL && content != NULL) {
2198 		/* update a description */
2199 		xmlNodeSetContent(node, (xmlChar *)content);
2200 	} else if (node != NULL && content == NULL) {
2201 		/* remove an existing description */
2202 		xmlUnlinkNode(node);
2203 		xmlFreeNode(node);
2204 	}
2205 	if (group != NULL && is_persistent((sa_group_t)share)) {
2206 		sa_handle_impl_t impl_handle;
2207 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2208 		if (impl_handle != NULL) {
2209 			ret = sa_commit_share(impl_handle->scfhandle, group,
2210 			    share);
2211 		} else {
2212 			ret = SA_SYSTEM_ERR;
2213 		}
2214 	}
2215 	return (ret);
2216 }
2217 
2218 /*
2219  * fixproblemchars(string)
2220  *
2221  * don't want any newline or tab characters in the text since these
2222  * could break display of data and legacy file formats.
2223  */
2224 static void
2225 fixproblemchars(char *str)
2226 {
2227 	int c;
2228 	for (c = *str; c != '\0'; c = *++str) {
2229 		if (c == '\t' || c == '\n')
2230 			*str = ' ';
2231 		else if (c == '"')
2232 			*str = '\'';
2233 	}
2234 }
2235 
2236 /*
2237  * sa_get_share_description(share)
2238  *
2239  * Return the description text for the specified share if it
2240  * exists. NULL if no description exists.
2241  */
2242 
2243 char *
2244 sa_get_share_description(sa_share_t share)
2245 {
2246 	xmlChar *description = NULL;
2247 	xmlNodePtr node;
2248 
2249 	for (node = ((xmlNodePtr)share)->children; node != NULL;
2250 	    node = node->next) {
2251 		if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2252 			break;
2253 		}
2254 	}
2255 	if (node != NULL) {
2256 		description = xmlNodeGetContent((xmlNodePtr)share);
2257 		fixproblemchars((char *)description);
2258 	}
2259 	return ((char *)description);
2260 }
2261 
2262 /*
2263  * sa_free(share_description(description)
2264  *
2265  * Free the description string.
2266  */
2267 
2268 void
2269 sa_free_share_description(char *description)
2270 {
2271 	xmlFree((xmlChar *)description);
2272 }
2273 
2274 /*
2275  * sa_create_optionset(group, proto)
2276  *
2277  * Create an optionset for the specified protocol in the specied
2278  * group. This is manifested as a property group within SMF.
2279  */
2280 
2281 sa_optionset_t
2282 sa_create_optionset(sa_group_t group, char *proto)
2283 {
2284 	sa_optionset_t optionset;
2285 	sa_group_t parent = group;
2286 
2287 	optionset = sa_get_optionset(group, proto);
2288 	if (optionset != NULL) {
2289 		/* can't have a duplicate protocol */
2290 		optionset = NULL;
2291 	} else {
2292 		optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group,
2293 		    NULL, (xmlChar *)"optionset", NULL);
2294 		/*
2295 		 * only put to repository if on a group and we were
2296 		 * able to create an optionset.
2297 		 */
2298 		if (optionset != NULL) {
2299 			char oname[SA_STRSIZE];
2300 			char *groupname;
2301 			char *id = NULL;
2302 
2303 			if (sa_is_share(group))
2304 				parent = sa_get_parent_group((sa_share_t)group);
2305 
2306 			sa_set_optionset_attr(optionset, "type", proto);
2307 
2308 			if (sa_is_share(group)) {
2309 				id = sa_get_share_attr((sa_share_t)group, "id");
2310 			}
2311 			(void) sa_optionset_name(optionset, oname,
2312 			    sizeof (oname), id);
2313 			groupname = sa_get_group_attr(parent, "name");
2314 			if (groupname != NULL && is_persistent(group)) {
2315 				sa_handle_impl_t impl_handle;
2316 				impl_handle = (sa_handle_impl_t)
2317 				    sa_find_group_handle(group);
2318 				assert(impl_handle != NULL);
2319 				if (impl_handle != NULL) {
2320 					(void) sa_get_instance(
2321 					    impl_handle->scfhandle,
2322 					    groupname);
2323 					(void) sa_create_pgroup(
2324 					    impl_handle->scfhandle, oname);
2325 				}
2326 			}
2327 			if (groupname != NULL)
2328 				sa_free_attr_string(groupname);
2329 			if (id != NULL)
2330 				sa_free_attr_string(id);
2331 		}
2332 	}
2333 	return (optionset);
2334 }
2335 
2336 /*
2337  * sa_get_property_parent(property)
2338  *
2339  * Given a property, return the object it is a property of. This will
2340  * be an optionset of some type.
2341  */
2342 
2343 static sa_optionset_t
2344 sa_get_property_parent(sa_property_t property)
2345 {
2346 	xmlNodePtr node = NULL;
2347 
2348 	if (property != NULL)
2349 		node = ((xmlNodePtr)property)->parent;
2350 	return ((sa_optionset_t)node);
2351 }
2352 
2353 /*
2354  * sa_get_optionset_parent(optionset)
2355  *
2356  * Return the parent of the specified optionset. This could be a group
2357  * or a share.
2358  */
2359 
2360 static sa_group_t
2361 sa_get_optionset_parent(sa_optionset_t optionset)
2362 {
2363 	xmlNodePtr node = NULL;
2364 
2365 	if (optionset != NULL)
2366 		node = ((xmlNodePtr)optionset)->parent;
2367 	return ((sa_group_t)node);
2368 }
2369 
2370 /*
2371  * zfs_needs_update(share)
2372  *
2373  * In order to avoid making multiple updates to a ZFS share when
2374  * setting properties, the share attribute "changed" will be set to
2375  * true when a property is added or modifed.  When done adding
2376  * properties, we can then detect that an update is needed.  We then
2377  * clear the state here to detect additional changes.
2378  */
2379 
2380 static int
2381 zfs_needs_update(sa_share_t share)
2382 {
2383 	char *attr;
2384 	int result = 0;
2385 
2386 	attr = sa_get_share_attr(share, "changed");
2387 	if (attr != NULL) {
2388 		sa_free_attr_string(attr);
2389 		result = 1;
2390 	}
2391 	set_node_attr((void *)share, "changed", NULL);
2392 	return (result);
2393 }
2394 
2395 /*
2396  * zfs_set_update(share)
2397  *
2398  * Set the changed attribute of the share to true.
2399  */
2400 
2401 static void
2402 zfs_set_update(sa_share_t share)
2403 {
2404 	set_node_attr((void *)share, "changed", "true");
2405 }
2406 
2407 /*
2408  * sa_commit_properties(optionset, clear)
2409  *
2410  * Check if SMF or ZFS config and either update or abort the pending
2411  * changes.
2412  */
2413 
2414 int
2415 sa_commit_properties(sa_optionset_t optionset, int clear)
2416 {
2417 	sa_group_t group;
2418 	sa_group_t parent;
2419 	int zfs = 0;
2420 	int needsupdate = 0;
2421 	int ret = SA_OK;
2422 	sa_handle_impl_t impl_handle;
2423 
2424 	group = sa_get_optionset_parent(optionset);
2425 	if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) {
2426 		/* only update ZFS if on a share */
2427 		parent = sa_get_parent_group(group);
2428 		zfs++;
2429 		if (parent != NULL && is_zfs_group(parent))
2430 			needsupdate = zfs_needs_update(group);
2431 		else
2432 			zfs = 0;
2433 	}
2434 	if (zfs) {
2435 		if (!clear && needsupdate)
2436 			ret = sa_zfs_update((sa_share_t)group);
2437 	} else {
2438 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2439 		if (impl_handle != NULL) {
2440 			if (clear) {
2441 				(void) sa_abort_transaction(
2442 				    impl_handle->scfhandle);
2443 			} else {
2444 				ret = sa_end_transaction(
2445 				    impl_handle->scfhandle);
2446 			}
2447 		} else {
2448 			ret = SA_SYSTEM_ERR;
2449 		}
2450 	}
2451 	return (ret);
2452 }
2453 
2454 /*
2455  * sa_destroy_optionset(optionset)
2456  *
2457  * Remove the optionset from its group. Update the repostory to
2458  * reflect this change.
2459  */
2460 
2461 int
2462 sa_destroy_optionset(sa_optionset_t optionset)
2463 {
2464 	char name[SA_STRSIZE];
2465 	int len;
2466 	int ret;
2467 	char *id = NULL;
2468 	sa_group_t group;
2469 	int ispersist = 1;
2470 
2471 	/* now delete the prop group */
2472 	group = sa_get_optionset_parent(optionset);
2473 	if (group != NULL && sa_is_share(group)) {
2474 		ispersist = is_persistent(group);
2475 		id = sa_get_share_attr((sa_share_t)group, "id");
2476 	}
2477 	if (ispersist) {
2478 		sa_handle_impl_t impl_handle;
2479 		len = sa_optionset_name(optionset, name, sizeof (name), id);
2480 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2481 		if (impl_handle != NULL) {
2482 			if (len > 0) {
2483 				ret = sa_delete_pgroup(impl_handle->scfhandle,
2484 				    name);
2485 			}
2486 		} else {
2487 			ret = SA_SYSTEM_ERR;
2488 		}
2489 	}
2490 	xmlUnlinkNode((xmlNodePtr)optionset);
2491 	xmlFreeNode((xmlNodePtr)optionset);
2492 	if (id != NULL)
2493 		sa_free_attr_string(id);
2494 	return (ret);
2495 }
2496 
2497 /* private to the implementation */
2498 int
2499 _sa_remove_optionset(sa_optionset_t optionset)
2500 {
2501 	int ret = SA_OK;
2502 
2503 	xmlUnlinkNode((xmlNodePtr)optionset);
2504 	xmlFreeNode((xmlNodePtr)optionset);
2505 	return (ret);
2506 }
2507 
2508 /*
2509  * sa_create_security(group, sectype, proto)
2510  *
2511  * Create a security optionset (one that has a type name and a
2512  * proto). Security is left over from a pure NFS implementation. The
2513  * naming will change in the future when the API is released.
2514  */
2515 sa_security_t
2516 sa_create_security(sa_group_t group, char *sectype, char *proto)
2517 {
2518 	sa_security_t security;
2519 	char *id = NULL;
2520 	sa_group_t parent;
2521 	char *groupname = NULL;
2522 
2523 	if (group != NULL && sa_is_share(group)) {
2524 		id = sa_get_share_attr((sa_share_t)group, "id");
2525 		parent = sa_get_parent_group(group);
2526 		if (parent != NULL)
2527 			groupname = sa_get_group_attr(parent, "name");
2528 	} else if (group != NULL) {
2529 		groupname = sa_get_group_attr(group, "name");
2530 	}
2531 
2532 	security = sa_get_security(group, sectype, proto);
2533 	if (security != NULL) {
2534 		/* can't have a duplicate security option */
2535 		security = NULL;
2536 	} else {
2537 		security = (sa_security_t)xmlNewChild((xmlNodePtr)group,
2538 		    NULL, (xmlChar *)"security", NULL);
2539 		if (security != NULL) {
2540 			char oname[SA_STRSIZE];
2541 			sa_set_security_attr(security, "type", proto);
2542 
2543 			sa_set_security_attr(security, "sectype", sectype);
2544 			(void) sa_security_name(security, oname,
2545 			    sizeof (oname), id);
2546 			if (groupname != NULL && is_persistent(group)) {
2547 				sa_handle_impl_t impl_handle;
2548 				impl_handle =
2549 				    (sa_handle_impl_t)sa_find_group_handle(
2550 				    group);
2551 				if (impl_handle != NULL) {
2552 					(void) sa_get_instance(
2553 					    impl_handle->scfhandle, groupname);
2554 					(void) sa_create_pgroup(
2555 					    impl_handle->scfhandle, oname);
2556 				}
2557 			}
2558 		}
2559 	}
2560 	if (groupname != NULL)
2561 		sa_free_attr_string(groupname);
2562 	return (security);
2563 }
2564 
2565 /*
2566  * sa_destroy_security(security)
2567  *
2568  * Remove the specified optionset from the document and the
2569  * configuration.
2570  */
2571 
2572 int
2573 sa_destroy_security(sa_security_t security)
2574 {
2575 	char name[SA_STRSIZE];
2576 	int len;
2577 	int ret = SA_OK;
2578 	char *id = NULL;
2579 	sa_group_t group;
2580 	int iszfs = 0;
2581 	int ispersist = 1;
2582 
2583 	group = sa_get_optionset_parent(security);
2584 
2585 	if (group != NULL)
2586 		iszfs = sa_group_is_zfs(group);
2587 
2588 	if (group != NULL && !iszfs) {
2589 		if (sa_is_share(group))
2590 			ispersist = is_persistent(group);
2591 		id = sa_get_share_attr((sa_share_t)group, "id");
2592 	}
2593 	if (ispersist) {
2594 		len = sa_security_name(security, name, sizeof (name), id);
2595 		if (!iszfs && len > 0) {
2596 			sa_handle_impl_t impl_handle;
2597 			impl_handle =
2598 			    (sa_handle_impl_t)sa_find_group_handle(group);
2599 			if (impl_handle != NULL) {
2600 				ret = sa_delete_pgroup(impl_handle->scfhandle,
2601 				    name);
2602 			} else {
2603 				ret = SA_SYSTEM_ERR;
2604 			}
2605 		}
2606 	}
2607 	xmlUnlinkNode((xmlNodePtr)security);
2608 	xmlFreeNode((xmlNodePtr)security);
2609 	if (iszfs)
2610 		ret = sa_zfs_update(group);
2611 	if (id != NULL)
2612 		sa_free_attr_string(id);
2613 	return (ret);
2614 }
2615 
2616 /*
2617  * sa_get_security_attr(optionset, tag)
2618  *
2619  * Return the specified attribute value from the optionset.
2620  */
2621 
2622 char *
2623 sa_get_security_attr(sa_property_t optionset, char *tag)
2624 {
2625 	return (get_node_attr((void *)optionset, tag));
2626 
2627 }
2628 
2629 /*
2630  * sa_set_security_attr(optionset, tag, value)
2631  *
2632  * Set the optioset attribute specied by tag to the specified value.
2633  */
2634 
2635 void
2636 sa_set_security_attr(sa_group_t optionset, char *tag, char *value)
2637 {
2638 	set_node_attr((void *)optionset, tag, value);
2639 }
2640 
2641 /*
2642  * is_nodetype(node, type)
2643  *
2644  * Check to see if node is of the type specified.
2645  */
2646 
2647 static int
2648 is_nodetype(void *node, char *type)
2649 {
2650 	return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0);
2651 }
2652 
2653 
2654 /*
2655  * add_or_update()
2656  *
2657  * Add or update a property. Pulled out of sa_set_prop_by_prop for
2658  * readability.
2659  */
2660 static int
2661 add_or_update(scfutilhandle_t *scf_handle, int type, scf_value_t *value,
2662     scf_transaction_entry_t *entry, char *name, char *valstr)
2663 {
2664 	int ret = SA_SYSTEM_ERR;
2665 
2666 	if (value != NULL) {
2667 		if (type == SA_PROP_OP_ADD)
2668 			ret = scf_transaction_property_new(scf_handle->trans,
2669 			    entry, name, SCF_TYPE_ASTRING);
2670 		else
2671 			ret = scf_transaction_property_change(scf_handle->trans,
2672 			    entry, name, SCF_TYPE_ASTRING);
2673 		if (ret == 0) {
2674 			ret = scf_value_set_astring(value, valstr);
2675 			if (ret == 0)
2676 				ret = scf_entry_add_value(entry, value);
2677 			if (ret == 0)
2678 				return (ret);
2679 			scf_value_destroy(value);
2680 		} else {
2681 			scf_entry_destroy(entry);
2682 		}
2683 	}
2684 	return (SA_SYSTEM_ERR);
2685 }
2686 
2687 /*
2688  * sa_set_prop_by_prop(optionset, group, prop, type)
2689  *
2690  * Add/remove/update the specified property prop into the optionset or
2691  * share. If a share, sort out which property group based on GUID. In
2692  * all cases, the appropriate transaction is set (or ZFS share is
2693  * marked as needing an update)
2694  */
2695 
2696 static int
2697 sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
2698 			sa_property_t prop, int type)
2699 {
2700 	char *name;
2701 	char *valstr;
2702 	int ret = SA_OK;
2703 	scf_transaction_entry_t *entry;
2704 	scf_value_t *value;
2705 	int opttype; /* 1 == optionset, 0 == security */
2706 	char *id = NULL;
2707 	int iszfs = 0;
2708 	int isshare = 0;
2709 	sa_group_t parent = NULL;
2710 	sa_handle_impl_t impl_handle;
2711 	scfutilhandle_t  *scf_handle;
2712 
2713 	if (!is_persistent(group)) {
2714 		/*
2715 		 * if the group/share is not persistent we don't need
2716 		 * to do anything here
2717 		 */
2718 		return (SA_OK);
2719 	}
2720 	impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2721 	if (impl_handle == NULL || impl_handle->scfhandle == NULL)
2722 		return (SA_SYSTEM_ERR);
2723 	scf_handle = impl_handle->scfhandle;
2724 	name = sa_get_property_attr(prop, "type");
2725 	valstr = sa_get_property_attr(prop, "value");
2726 	entry = scf_entry_create(scf_handle->handle);
2727 	opttype = is_nodetype((void *)optionset, "optionset");
2728 
2729 	if (valstr != NULL && entry != NULL) {
2730 		if (sa_is_share(group)) {
2731 			isshare = 1;
2732 			parent = sa_get_parent_group(group);
2733 			if (parent != NULL)
2734 				iszfs = is_zfs_group(parent);
2735 		} else {
2736 			iszfs = is_zfs_group(group);
2737 		}
2738 		if (!iszfs) {
2739 			if (scf_handle->trans == NULL) {
2740 				char oname[SA_STRSIZE];
2741 				char *groupname = NULL;
2742 				if (isshare) {
2743 					if (parent != NULL) {
2744 						groupname =
2745 						    sa_get_group_attr(parent,
2746 						    "name");
2747 					}
2748 					id =
2749 					    sa_get_share_attr((sa_share_t)group,
2750 					    "id");
2751 				} else {
2752 					groupname = sa_get_group_attr(group,
2753 					    "name");
2754 				}
2755 				if (groupname != NULL) {
2756 					ret = sa_get_instance(scf_handle,
2757 					    groupname);
2758 					sa_free_attr_string(groupname);
2759 				}
2760 				if (opttype)
2761 					(void) sa_optionset_name(optionset,
2762 					    oname, sizeof (oname), id);
2763 				else
2764 					(void) sa_security_name(optionset,
2765 					    oname, sizeof (oname), id);
2766 				ret = sa_start_transaction(scf_handle, oname);
2767 			}
2768 			if (ret == SA_OK) {
2769 				switch (type) {
2770 				case SA_PROP_OP_REMOVE:
2771 					ret = scf_transaction_property_delete(
2772 					    scf_handle->trans, entry, name);
2773 					break;
2774 				case SA_PROP_OP_ADD:
2775 				case SA_PROP_OP_UPDATE:
2776 					value = scf_value_create(
2777 					    scf_handle->handle);
2778 					ret = add_or_update(scf_handle, type,
2779 					    value, entry, name, valstr);
2780 					break;
2781 				}
2782 			}
2783 		} else {
2784 			/*
2785 			 * ZFS update. The calling function would have updated
2786 			 * the internal XML structure. Just need to flag it as
2787 			 * changed for ZFS.
2788 			 */
2789 			zfs_set_update((sa_share_t)group);
2790 		}
2791 	}
2792 
2793 	if (name != NULL)
2794 		sa_free_attr_string(name);
2795 	if (valstr != NULL)
2796 		sa_free_attr_string(valstr);
2797 	else if (entry != NULL)
2798 		scf_entry_destroy(entry);
2799 
2800 	if (ret == -1)
2801 		ret = SA_SYSTEM_ERR;
2802 
2803 	return (ret);
2804 }
2805 
2806 /*
2807  * sa_create_property(name, value)
2808  *
2809  * Create a new property with the specified name and value.
2810  */
2811 
2812 sa_property_t
2813 sa_create_property(char *name, char *value)
2814 {
2815 	xmlNodePtr node;
2816 
2817 	node = xmlNewNode(NULL, (xmlChar *)"option");
2818 	if (node != NULL) {
2819 		xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name);
2820 		xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value);
2821 	}
2822 	return ((sa_property_t)node);
2823 }
2824 
2825 /*
2826  * sa_add_property(object, property)
2827  *
2828  * Add the specified property to the object. Issue the appropriate
2829  * transaction or mark a ZFS object as needing an update.
2830  */
2831 
2832 int
2833 sa_add_property(void *object, sa_property_t property)
2834 {
2835 	int ret = SA_OK;
2836 	sa_group_t parent;
2837 	sa_group_t group;
2838 	char *proto;
2839 
2840 	proto = sa_get_optionset_attr(object, "type");
2841 	if (property != NULL) {
2842 		if ((ret = sa_valid_property(object, proto, property)) ==
2843 		    SA_OK) {
2844 			property = (sa_property_t)xmlAddChild(
2845 			    (xmlNodePtr)object, (xmlNodePtr)property);
2846 		} else {
2847 			if (proto != NULL)
2848 				sa_free_attr_string(proto);
2849 			return (ret);
2850 		}
2851 	}
2852 
2853 	if (proto != NULL)
2854 		sa_free_attr_string(proto);
2855 
2856 	parent = sa_get_parent_group(object);
2857 	if (!is_persistent(parent)) {
2858 		return (ret);
2859 	}
2860 
2861 	if (sa_is_share(parent))
2862 		group = sa_get_parent_group(parent);
2863 	else
2864 		group = parent;
2865 
2866 	if (property == NULL) {
2867 		ret = SA_NO_MEMORY;
2868 	} else {
2869 		char oname[SA_STRSIZE];
2870 
2871 		if (!is_zfs_group(group)) {
2872 			char *id = NULL;
2873 			sa_handle_impl_t impl_handle;
2874 			scfutilhandle_t  *scf_handle;
2875 
2876 			impl_handle = (sa_handle_impl_t)sa_find_group_handle(
2877 			    group);
2878 			if (impl_handle == NULL ||
2879 			    impl_handle->scfhandle == NULL)
2880 				ret = SA_SYSTEM_ERR;
2881 			if (ret == SA_OK) {
2882 				scf_handle = impl_handle->scfhandle;
2883 				if (sa_is_share((sa_group_t)parent)) {
2884 					id = sa_get_share_attr(
2885 					    (sa_share_t)parent, "id");
2886 				}
2887 				if (scf_handle->trans == NULL) {
2888 					if (is_nodetype(object, "optionset")) {
2889 						(void) sa_optionset_name(
2890 						    (sa_optionset_t)object,
2891 						    oname, sizeof (oname), id);
2892 					} else {
2893 						(void) sa_security_name(
2894 						    (sa_optionset_t)object,
2895 						    oname, sizeof (oname), id);
2896 					}
2897 					ret = sa_start_transaction(scf_handle,
2898 					    oname);
2899 				}
2900 				if (ret == SA_OK) {
2901 					char *name;
2902 					char *value;
2903 					name = sa_get_property_attr(property,
2904 					    "type");
2905 					value = sa_get_property_attr(property,
2906 					    "value");
2907 					if (name != NULL && value != NULL) {
2908 						if (scf_handle->scf_state ==
2909 						    SCH_STATE_INIT) {
2910 							ret = sa_set_property(
2911 							    scf_handle, name,
2912 							    value);
2913 						}
2914 					} else {
2915 						ret = SA_CONFIG_ERR;
2916 					}
2917 					if (name != NULL)
2918 						sa_free_attr_string(
2919 						    name);
2920 					if (value != NULL)
2921 						sa_free_attr_string(value);
2922 				}
2923 				if (id != NULL)
2924 					sa_free_attr_string(id);
2925 			}
2926 		} else {
2927 			/*
2928 			 * ZFS is a special case. We do want
2929 			 * to allow editing property/security
2930 			 * lists since we can have a better
2931 			 * syntax and we also want to keep
2932 			 * things consistent when possible.
2933 			 *
2934 			 * Right now, we defer until the
2935 			 * sa_commit_properties so we can get
2936 			 * them all at once. We do need to
2937 			 * mark the share as "changed"
2938 			 */
2939 			zfs_set_update((sa_share_t)parent);
2940 		}
2941 	}
2942 	return (ret);
2943 }
2944 
2945 /*
2946  * sa_remove_property(property)
2947  *
2948  * Remove the specied property from its containing object. Update the
2949  * repository as appropriate.
2950  */
2951 
2952 int
2953 sa_remove_property(sa_property_t property)
2954 {
2955 	int ret = SA_OK;
2956 
2957 	if (property != NULL) {
2958 		sa_optionset_t optionset;
2959 		sa_group_t group;
2960 		optionset = sa_get_property_parent(property);
2961 		if (optionset != NULL) {
2962 			group = sa_get_optionset_parent(optionset);
2963 			if (group != NULL) {
2964 				ret = sa_set_prop_by_prop(optionset, group,
2965 				    property, SA_PROP_OP_REMOVE);
2966 			}
2967 		}
2968 		xmlUnlinkNode((xmlNodePtr)property);
2969 		xmlFreeNode((xmlNodePtr)property);
2970 	} else {
2971 		ret = SA_NO_SUCH_PROP;
2972 	}
2973 	return (ret);
2974 }
2975 
2976 /*
2977  * sa_update_property(property, value)
2978  *
2979  * Update the specified property to the new value.  If value is NULL,
2980  * we currently treat this as a remove.
2981  */
2982 
2983 int
2984 sa_update_property(sa_property_t property, char *value)
2985 {
2986 	int ret = SA_OK;
2987 	if (value == NULL) {
2988 		return (sa_remove_property(property));
2989 	} else {
2990 		sa_optionset_t optionset;
2991 		sa_group_t group;
2992 		set_node_attr((void *)property, "value", value);
2993 		optionset = sa_get_property_parent(property);
2994 		if (optionset != NULL) {
2995 			group = sa_get_optionset_parent(optionset);
2996 			if (group != NULL) {
2997 				ret = sa_set_prop_by_prop(optionset, group,
2998 				    property, SA_PROP_OP_UPDATE);
2999 			}
3000 		} else {
3001 			ret = SA_NO_SUCH_PROP;
3002 		}
3003 	}
3004 	return (ret);
3005 }
3006 
3007 /*
3008  * sa_get_protocol_property(propset, prop)
3009  *
3010  * Get the specified protocol specific property. These are global to
3011  * the protocol and not specific to a group or share.
3012  */
3013 
3014 sa_property_t
3015 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop)
3016 {
3017 	xmlNodePtr node = (xmlNodePtr)propset;
3018 	xmlChar *value = NULL;
3019 
3020 	for (node = node->children; node != NULL;
3021 	    node = node->next) {
3022 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3023 			if (prop == NULL)
3024 				break;
3025 			value = xmlGetProp(node, (xmlChar *)"type");
3026 			if (value != NULL &&
3027 			    xmlStrcasecmp(value, (xmlChar *)prop) == 0) {
3028 				break;
3029 			}
3030 			if (value != NULL) {
3031 				xmlFree(value);
3032 				value = NULL;
3033 			}
3034 		}
3035 	}
3036 	if (value != NULL)
3037 		xmlFree(value);
3038 	if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
3039 		/*
3040 		 * avoid a non option node -- it is possible to be a
3041 		 * text node
3042 		 */
3043 		node = NULL;
3044 	}
3045 	return ((sa_property_t)node);
3046 }
3047 
3048 /*
3049  * sa_get_next_protocol_property(prop)
3050  *
3051  * Get the next protocol specific property in the list.
3052  */
3053 
3054 sa_property_t
3055 sa_get_next_protocol_property(sa_property_t prop)
3056 {
3057 	xmlNodePtr node;
3058 
3059 	for (node = ((xmlNodePtr)prop)->next; node != NULL;
3060 	    node = node->next) {
3061 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3062 			break;
3063 		}
3064 	}
3065 	return ((sa_property_t)node);
3066 }
3067 
3068 /*
3069  * sa_set_protocol_property(prop, value)
3070  *
3071  * Set the specified property to have the new value.  The protocol
3072  * specific plugin will then be called to update the property.
3073  */
3074 
3075 int
3076 sa_set_protocol_property(sa_property_t prop, char *value)
3077 {
3078 	sa_protocol_properties_t propset;
3079 	char *proto;
3080 	int ret = SA_INVALID_PROTOCOL;
3081 
3082 	propset = ((xmlNodePtr)prop)->parent;
3083 	if (propset != NULL) {
3084 		proto = sa_get_optionset_attr(propset, "type");
3085 		if (proto != NULL) {
3086 			set_node_attr((xmlNodePtr)prop, "value", value);
3087 			ret = sa_proto_set_property(proto, prop);
3088 			sa_free_attr_string(proto);
3089 		}
3090 	}
3091 	return (ret);
3092 }
3093 
3094 /*
3095  * sa_add_protocol_property(propset, prop)
3096  *
3097  * Add a new property to the protocol sepcific property set.
3098  */
3099 
3100 int
3101 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop)
3102 {
3103 	xmlNodePtr node;
3104 
3105 	/* should check for legitimacy */
3106 	node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop);
3107 	if (node != NULL)
3108 		return (SA_OK);
3109 	return (SA_NO_MEMORY);
3110 }
3111 
3112 /*
3113  * sa_create_protocol_properties(proto)
3114  *
3115  * Create a protocol specifity property set.
3116  */
3117 
3118 sa_protocol_properties_t
3119 sa_create_protocol_properties(char *proto)
3120 {
3121 	xmlNodePtr node;
3122 
3123 	node = xmlNewNode(NULL, (xmlChar *)"propertyset");
3124 	if (node != NULL)
3125 		xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
3126 	return (node);
3127 }
3128