xref: /titanic_41/usr/src/lib/libshare/common/libshare.c (revision 7e6639c297bb2c00e0c6dc8a83143d2201c6b51b)
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 			if (group != NULL) {
978 				subgroup = strchr(group, '/');
979 				if (subgroup != NULL)
980 					*subgroup++ = '\0';
981 			}
982 		}
983 		/*
984 		 * We want to find the, possibly, named group. If
985 		 * group is not NULL, then lookup the name. If it is
986 		 * NULL, we only do the find if groupname is also
987 		 * NULL. This allows lookup of the "first" group in
988 		 * the internal list.
989 		 */
990 		if (group != NULL || groupname == NULL)
991 			node = find_group_by_name(impl_handle->tree,
992 			    (xmlChar *)group);
993 
994 		/* if a subgroup, find it before returning */
995 		if (subgroup != NULL && node != NULL)
996 			node = find_group_by_name(node, (xmlChar *)subgroup);
997 	}
998 	if (node != NULL && (char *)group != NULL)
999 		(void) sa_get_instance(impl_handle->scfhandle, (char *)group);
1000 	if (group != NULL)
1001 		free(group);
1002 	return ((sa_group_t)(node));
1003 }
1004 
1005 /*
1006  * sa_get_next_group(group)
1007  *	Return the "next" group after the specified group from
1008  *	the internal group list.  NULL if there are no more.
1009  */
1010 sa_group_t
1011 sa_get_next_group(sa_group_t group)
1012 {
1013 	xmlNodePtr ngroup = NULL;
1014 	if (group != NULL) {
1015 		for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL;
1016 		    ngroup = ngroup->next) {
1017 			if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0)
1018 				break;
1019 		}
1020 	}
1021 	return ((sa_group_t)ngroup);
1022 }
1023 
1024 /*
1025  * sa_get_share(group, sharepath)
1026  *	Return the share object for the share specified. The share
1027  *	must be in the specified group.  Return NULL if not found.
1028  */
1029 sa_share_t
1030 sa_get_share(sa_group_t group, char *sharepath)
1031 {
1032 	xmlNodePtr node = NULL;
1033 	xmlChar *path;
1034 
1035 	/*
1036 	 * For future scalability, this should end up building a cache
1037 	 * since it will get called regularly by the mountd and info
1038 	 * services.
1039 	 */
1040 	if (group != NULL) {
1041 		for (node = ((xmlNodePtr)group)->children; node != NULL;
1042 		    node = node->next) {
1043 			if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1044 				if (sharepath == NULL) {
1045 					break;
1046 				} else {
1047 					/* is it the correct share? */
1048 					path = xmlGetProp(node,
1049 					    (xmlChar *)"path");
1050 					if (path != NULL &&
1051 					    xmlStrcmp(path,
1052 					    (xmlChar *)sharepath) == 0) {
1053 						xmlFree(path);
1054 						break;
1055 					}
1056 					xmlFree(path);
1057 				}
1058 			}
1059 		}
1060 	}
1061 	return ((sa_share_t)node);
1062 }
1063 
1064 /*
1065  * sa_get_next_share(share)
1066  *	Return the next share following the specified share
1067  *	from the internal list of shares. Returns NULL if there
1068  *	are no more shares.  The list is relative to the same
1069  *	group.
1070  */
1071 sa_share_t
1072 sa_get_next_share(sa_share_t share)
1073 {
1074 	xmlNodePtr node = NULL;
1075 
1076 	if (share != NULL) {
1077 		for (node = ((xmlNodePtr)share)->next; node != NULL;
1078 		    node = node->next) {
1079 			if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) {
1080 				break;
1081 			}
1082 		}
1083 	}
1084 	return ((sa_share_t)node);
1085 }
1086 
1087 /*
1088  * _sa_get_child_node(node, type)
1089  *
1090  * find the child node of the specified node that has "type". This is
1091  * used to implement several internal functions.
1092  */
1093 
1094 static xmlNodePtr
1095 _sa_get_child_node(xmlNodePtr node, xmlChar *type)
1096 {
1097 	xmlNodePtr child;
1098 	for (child = node->xmlChildrenNode; child != NULL;
1099 	    child = child->next)
1100 		if (xmlStrcmp(child->name, type) == 0)
1101 			return (child);
1102 	return ((xmlNodePtr)NULL);
1103 }
1104 
1105 /*
1106  *  find_share(group, path)
1107  *
1108  * Search all the shares in the specified group for one that has the
1109  * specified path.
1110  */
1111 
1112 static sa_share_t
1113 find_share(sa_group_t group, char *sharepath)
1114 {
1115 	sa_share_t share;
1116 	char *path;
1117 
1118 	for (share = sa_get_share(group, NULL); share != NULL;
1119 	    share = sa_get_next_share(share)) {
1120 		path = sa_get_share_attr(share, "path");
1121 		if (path != NULL && strcmp(path, sharepath) == 0) {
1122 			sa_free_attr_string(path);
1123 			break;
1124 		}
1125 		if (path != NULL)
1126 			sa_free_attr_string(path);
1127 	}
1128 	return (share);
1129 }
1130 
1131 /*
1132  * sa_get_sub_group(group)
1133  *
1134  * Get the first sub-group of group. The sa_get_next_group() function
1135  * can be used to get the rest. This is currently only used for ZFS
1136  * sub-groups but could be used to implement a more general mechanism.
1137  */
1138 
1139 sa_group_t
1140 sa_get_sub_group(sa_group_t group)
1141 {
1142 	return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1143 	    (xmlChar *)"group"));
1144 }
1145 
1146 /*
1147  * sa_find_share(sharepath)
1148  *	Finds a share regardless of group.  In the future, this
1149  *	function should utilize a cache and hash table of some kind.
1150  *	The current assumption is that a path will only be shared
1151  *	once.  In the future, this may change as implementation of
1152  *	resource names comes into being.
1153  */
1154 sa_share_t
1155 sa_find_share(sa_handle_t handle, char *sharepath)
1156 {
1157 	sa_group_t group;
1158 	sa_group_t zgroup;
1159 	sa_share_t share = NULL;
1160 	int done = 0;
1161 
1162 	for (group = sa_get_group(handle, NULL); group != NULL && !done;
1163 	    group = sa_get_next_group(group)) {
1164 		if (is_zfs_group(group)) {
1165 			for (zgroup =
1166 			    (sa_group_t)_sa_get_child_node((xmlNodePtr)group,
1167 			    (xmlChar *)"group");
1168 			    zgroup != NULL;
1169 			    zgroup = sa_get_next_group(zgroup)) {
1170 				share = find_share(zgroup, sharepath);
1171 				if (share != NULL)
1172 					break;
1173 			}
1174 		} else {
1175 			share = find_share(group, sharepath);
1176 		}
1177 		if (share != NULL)
1178 			break;
1179 	}
1180 	return (share);
1181 }
1182 
1183 /*
1184  *  sa_check_path(group, path, strictness)
1185  *
1186  * check that path is a valid path relative to the group.  Currently,
1187  * we are ignoring the group and checking only the NFS rules. Later,
1188  * we may want to use the group to then check against the protocols
1189  * enabled on the group. The strictness values mean:
1190  * SA_CHECK_NORMAL == only check newpath against shares that are active
1191  * SA_CHECK_STRICT == check newpath against both active shares and those
1192  *		      stored in the repository
1193  */
1194 
1195 int
1196 sa_check_path(sa_group_t group, char *path, int strictness)
1197 {
1198 	sa_handle_t handle;
1199 
1200 	handle = sa_find_group_handle(group);
1201 	return (validpath(handle, path, strictness));
1202 }
1203 
1204 /*
1205  * _sa_add_share(group, sharepath, persist, *error)
1206  *
1207  * common code for all types of add_share. sa_add_share() is the
1208  * public API, we also need to be able to do this when parsing legacy
1209  * files and construction of the internal configuration while
1210  * extracting config info from SMF.
1211  */
1212 
1213 sa_share_t
1214 _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
1215 {
1216 	xmlNodePtr node = NULL;
1217 	int err;
1218 
1219 	err  = SA_OK; /* assume success */
1220 
1221 	node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"share", NULL);
1222 	if (node != NULL) {
1223 		xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath);
1224 		xmlSetProp(node, (xmlChar *)"type",
1225 		    persist ? (xmlChar *)"persist" : (xmlChar *)"transient");
1226 		if (persist != SA_SHARE_TRANSIENT) {
1227 			/*
1228 			 * persistent shares come in two flavors: SMF and
1229 			 * ZFS. Sort this one out based on target group and
1230 			 * path type. Currently, only NFS is supported in the
1231 			 * ZFS group and it is always on.
1232 			 */
1233 			if (sa_group_is_zfs(group) &&
1234 			    sa_path_is_zfs(sharepath)) {
1235 				err = sa_zfs_set_sharenfs(group, sharepath, 1);
1236 			} else {
1237 				sa_handle_impl_t impl_handle;
1238 				impl_handle =
1239 				    (sa_handle_impl_t)sa_find_group_handle(
1240 				    group);
1241 				if (impl_handle != NULL) {
1242 					err = sa_commit_share(
1243 					    impl_handle->scfhandle, group,
1244 					    (sa_share_t)node);
1245 				} else {
1246 					err = SA_SYSTEM_ERR;
1247 				}
1248 			}
1249 		}
1250 		if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) {
1251 			/* called by the dfstab parser so could be a show */
1252 			err = SA_OK;
1253 		}
1254 		if (err != SA_OK) {
1255 			/*
1256 			 * we couldn't commit to the repository so undo
1257 			 * our internal state to reflect reality.
1258 			 */
1259 			xmlUnlinkNode(node);
1260 			xmlFreeNode(node);
1261 			node = NULL;
1262 		}
1263 	} else {
1264 		err = SA_NO_MEMORY;
1265 	}
1266 	if (error != NULL)
1267 		*error = err;
1268 	return (node);
1269 }
1270 
1271 /*
1272  * sa_add_share(group, sharepath, persist, *error)
1273  *
1274  *	Add a new share object to the specified group.  The share will
1275  *	have the specified sharepath and will only be constructed if
1276  *	it is a valid path to be shared.  NULL is returned on error
1277  *	and a detailed error value will be returned via the error
1278  *	pointer.
1279  */
1280 sa_share_t
1281 sa_add_share(sa_group_t group, char *sharepath, int persist, int *error)
1282 {
1283 	xmlNodePtr node = NULL;
1284 	sa_share_t dup;
1285 	int strictness = SA_CHECK_NORMAL;
1286 	sa_handle_t handle;
1287 
1288 	/*
1289 	 * If the share is to be permanent, use strict checking so a
1290 	 * bad config doesn't get created. Transient shares only need
1291 	 * to check against the currently active
1292 	 * shares. SA_SHARE_PARSER is a modifier used internally to
1293 	 * indicate that we are being called by the dfstab parser and
1294 	 * that we need strict checking in all cases. Normally persist
1295 	 * is in integer value but SA_SHARE_PARSER may be or'd into
1296 	 * it as an override.
1297 	 */
1298 	if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT)
1299 		strictness = SA_CHECK_STRICT;
1300 
1301 	handle = sa_find_group_handle(group);
1302 
1303 	if ((dup = sa_find_share(handle, sharepath)) == NULL &&
1304 	    (*error = sa_check_path(group, sharepath, strictness)) == SA_OK) {
1305 		node = _sa_add_share(group, sharepath, persist, error);
1306 	}
1307 	if (dup != NULL)
1308 		*error = SA_DUPLICATE_NAME;
1309 
1310 	return ((sa_share_t)node);
1311 }
1312 
1313 /*
1314  * sa_enable_share(share, protocol)
1315  *	Enable the specified share to the specified protocol.
1316  *	If protocol is NULL, then all protocols.
1317  */
1318 int
1319 sa_enable_share(sa_share_t share, char *protocol)
1320 {
1321 	char *sharepath;
1322 	struct stat st;
1323 	int err = 0;
1324 
1325 	sharepath = sa_get_share_attr(share, "path");
1326 	if (stat(sharepath, &st) < 0) {
1327 		err = SA_NO_SUCH_PATH;
1328 	} else {
1329 		/* tell the server about the share */
1330 		if (protocol != NULL) {
1331 			/* lookup protocol specific handler */
1332 			err = sa_proto_share(protocol, share);
1333 			if (err == SA_OK)
1334 				(void) sa_set_share_attr(share, "shared",
1335 				    "true");
1336 		} else {
1337 			/*
1338 			 * Tell all protocols.  Only NFS for now but
1339 			 * SMB is coming.
1340 			 */
1341 			err = sa_proto_share("nfs", share);
1342 			(void) sa_set_share_attr(share, "shared", "true");
1343 		}
1344 	}
1345 	if (sharepath != NULL)
1346 		sa_free_attr_string(sharepath);
1347 	return (err);
1348 }
1349 
1350 /*
1351  * sa_disable_share(share, protocol)
1352  *	Disable the specified share to the specified protocol.
1353  *	If protocol is NULL, then all protocols.
1354  */
1355 int
1356 sa_disable_share(sa_share_t share, char *protocol)
1357 {
1358 	char *path;
1359 	char *shared;
1360 	int ret = SA_OK;
1361 
1362 	path = sa_get_share_attr(share, "path");
1363 	shared = sa_get_share_attr(share, "shared");
1364 
1365 	if (protocol != NULL) {
1366 		ret = sa_proto_unshare(protocol, path);
1367 	} else {
1368 		/* need to do all protocols */
1369 		ret = sa_proto_unshare("nfs", path);
1370 	}
1371 	if (ret == SA_OK)
1372 		(void) sa_set_share_attr(share, "shared", NULL);
1373 	if (path != NULL)
1374 		sa_free_attr_string(path);
1375 	if (shared != NULL)
1376 		sa_free_attr_string(shared);
1377 	return (ret);
1378 }
1379 
1380 /*
1381  * sa_remove_share(share)
1382  *
1383  * remove the specified share from its containing group.
1384  * Remove from the SMF or ZFS configuration space.
1385  */
1386 
1387 int
1388 sa_remove_share(sa_share_t share)
1389 {
1390 	sa_group_t group;
1391 	int ret = SA_OK;
1392 	char *type;
1393 	int transient = 0;
1394 	char *groupname;
1395 	char *zfs;
1396 
1397 	type = sa_get_share_attr(share, "type");
1398 	group = sa_get_parent_group(share);
1399 	zfs = sa_get_group_attr(group, "zfs");
1400 	groupname = sa_get_group_attr(group, "name");
1401 	if (type != NULL && strcmp(type, "persist") != 0)
1402 		transient = 1;
1403 	if (type != NULL)
1404 		sa_free_attr_string(type);
1405 
1406 	/* remove the node from its group then free the memory */
1407 
1408 	/*
1409 	 * need to test if "busy"
1410 	 */
1411 	/* only do SMF action if permanent */
1412 	if (!transient || zfs != NULL) {
1413 		/* remove from legacy dfstab as well as possible SMF */
1414 		ret = sa_delete_legacy(share);
1415 		if (ret == SA_OK) {
1416 			if (!sa_group_is_zfs(group)) {
1417 				sa_handle_impl_t impl_handle;
1418 				impl_handle = (sa_handle_impl_t)
1419 				    sa_find_group_handle(group);
1420 				if (impl_handle != NULL) {
1421 					ret = sa_delete_share(
1422 					    impl_handle->scfhandle, group,
1423 					    share);
1424 				} else {
1425 					ret = SA_SYSTEM_ERR;
1426 				}
1427 			} else {
1428 				char *sharepath = sa_get_share_attr(share,
1429 				    "path");
1430 				if (sharepath != NULL) {
1431 					ret = sa_zfs_set_sharenfs(group,
1432 					    sharepath, 0);
1433 					sa_free_attr_string(sharepath);
1434 				}
1435 			}
1436 		}
1437 	}
1438 	if (groupname != NULL)
1439 		sa_free_attr_string(groupname);
1440 	if (zfs != NULL)
1441 		sa_free_attr_string(zfs);
1442 
1443 	xmlUnlinkNode((xmlNodePtr)share);
1444 	xmlFreeNode((xmlNodePtr)share);
1445 	return (ret);
1446 }
1447 
1448 /*
1449  * sa_move_share(group, share)
1450  *
1451  * move the specified share to the specified group.  Update SMF
1452  * appropriately.
1453  */
1454 
1455 int
1456 sa_move_share(sa_group_t group, sa_share_t share)
1457 {
1458 	sa_group_t oldgroup;
1459 	int ret = SA_OK;
1460 
1461 	/* remove the node from its group then free the memory */
1462 
1463 	oldgroup = sa_get_parent_group(share);
1464 	if (oldgroup != group) {
1465 		sa_handle_impl_t impl_handle;
1466 		xmlUnlinkNode((xmlNodePtr)share);
1467 		/*
1468 		 * now that the share isn't in its old group, add to
1469 		 * the new one
1470 		 */
1471 		xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share);
1472 		/* need to deal with SMF */
1473 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1474 		if (impl_handle != NULL) {
1475 			/*
1476 			 * need to remove from old group first and then add to
1477 			 * new group. Ideally, we would do the other order but
1478 			 * need to avoid having the share in two groups at the
1479 			 * same time.
1480 			 */
1481 			ret = sa_delete_share(impl_handle->scfhandle, oldgroup,
1482 			    share);
1483 			if (ret == SA_OK)
1484 				ret = sa_commit_share(impl_handle->scfhandle,
1485 				    group, share);
1486 		} else {
1487 			ret = SA_SYSTEM_ERR;
1488 		}
1489 	}
1490 	return (ret);
1491 }
1492 
1493 /*
1494  * sa_get_parent_group(share)
1495  *
1496  * Return the containg group for the share. If a group was actually
1497  * passed in, we don't want a parent so return NULL.
1498  */
1499 
1500 sa_group_t
1501 sa_get_parent_group(sa_share_t share)
1502 {
1503 	xmlNodePtr node = NULL;
1504 	if (share != NULL) {
1505 		node = ((xmlNodePtr)share)->parent;
1506 		/*
1507 		 * make sure parent is a group and not sharecfg since
1508 		 * we may be cheating and passing in a group.
1509 		 * Eventually, groups of groups might come into being.
1510 		 */
1511 		if (node == NULL ||
1512 		    xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0)
1513 			node = NULL;
1514 	}
1515 	return ((sa_group_t)node);
1516 }
1517 
1518 /*
1519  * _sa_create_group(impl_handle, groupname)
1520  *
1521  * Create a group in the document. The caller will need to deal with
1522  * configuration store and activation.
1523  */
1524 
1525 sa_group_t
1526 _sa_create_group(sa_handle_impl_t impl_handle, char *groupname)
1527 {
1528 	xmlNodePtr node = NULL;
1529 
1530 	if (sa_valid_group_name(groupname)) {
1531 		node = xmlNewChild(impl_handle->tree, NULL, (xmlChar *)"group",
1532 		    NULL);
1533 		if (node != NULL) {
1534 			xmlSetProp(node, (xmlChar *)"name",
1535 			    (xmlChar *)groupname);
1536 			xmlSetProp(node, (xmlChar *)"state",
1537 			    (xmlChar *)"enabled");
1538 		}
1539 	}
1540 	return ((sa_group_t)node);
1541 }
1542 
1543 /*
1544  * _sa_create_zfs_group(group, groupname)
1545  *
1546  * Create a ZFS subgroup under the specified group. This may
1547  * eventually form the basis of general sub-groups, but is currently
1548  * restricted to ZFS.
1549  */
1550 sa_group_t
1551 _sa_create_zfs_group(sa_group_t group, char *groupname)
1552 {
1553 	xmlNodePtr node = NULL;
1554 
1555 	node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"group", NULL);
1556 	if (node != NULL) {
1557 		xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname);
1558 		xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled");
1559 	}
1560 
1561 	return ((sa_group_t)node);
1562 }
1563 
1564 /*
1565  * sa_create_group(groupname, *error)
1566  *
1567  * Create a new group with groupname.  Need to validate that it is a
1568  * legal name for SMF and the construct the SMF service instance of
1569  * svc:/network/shares/group to implement the group. All necessary
1570  * operational properties must be added to the group at this point
1571  * (via the SMF transaction model).
1572  */
1573 sa_group_t
1574 sa_create_group(sa_handle_t handle, char *groupname, int *error)
1575 {
1576 	xmlNodePtr node = NULL;
1577 	sa_group_t group;
1578 	int ret;
1579 	char rbacstr[SA_STRSIZE];
1580 	sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle;
1581 
1582 	ret = SA_OK;
1583 
1584 	if (impl_handle == NULL || impl_handle->scfhandle == NULL) {
1585 		ret = SA_SYSTEM_ERR;
1586 		goto err;
1587 	}
1588 
1589 	group = sa_get_group(handle, groupname);
1590 	if (group != NULL) {
1591 		ret = SA_DUPLICATE_NAME;
1592 	} else {
1593 		if (sa_valid_group_name(groupname)) {
1594 			node = xmlNewChild(impl_handle->tree, NULL,
1595 			    (xmlChar *)"group", NULL);
1596 			if (node != NULL) {
1597 				xmlSetProp(node, (xmlChar *)"name",
1598 				    (xmlChar *)groupname);
1599 				/* default to the group being enabled */
1600 				xmlSetProp(node, (xmlChar *)"state",
1601 				    (xmlChar *)"enabled");
1602 				ret = sa_create_instance(impl_handle->scfhandle,
1603 				    groupname);
1604 				if (ret == SA_OK) {
1605 					ret = sa_start_transaction(
1606 					    impl_handle->scfhandle,
1607 					    "operation");
1608 				}
1609 				if (ret == SA_OK) {
1610 					ret = sa_set_property(
1611 					    impl_handle->scfhandle,
1612 					    "state", "enabled");
1613 					if (ret == SA_OK) {
1614 						ret = sa_end_transaction(
1615 						    impl_handle->scfhandle);
1616 					} else {
1617 						sa_abort_transaction(
1618 						    impl_handle->scfhandle);
1619 					}
1620 				}
1621 				if (ret == SA_OK) {
1622 					/* initialize the RBAC strings */
1623 					ret = sa_start_transaction(
1624 					    impl_handle->scfhandle,
1625 					    "general");
1626 					if (ret == SA_OK) {
1627 						(void) snprintf(rbacstr,
1628 						    sizeof (rbacstr), "%s.%s",
1629 						    SA_RBAC_MANAGE, groupname);
1630 						ret = sa_set_property(
1631 						    impl_handle->scfhandle,
1632 						    "action_authorization",
1633 						    rbacstr);
1634 					}
1635 					if (ret == SA_OK) {
1636 						(void) snprintf(rbacstr,
1637 						    sizeof (rbacstr), "%s.%s",
1638 						    SA_RBAC_VALUE, groupname);
1639 						ret = sa_set_property(
1640 						    impl_handle->scfhandle,
1641 						    "value_authorization",
1642 						    rbacstr);
1643 					}
1644 					if (ret == SA_OK) {
1645 						ret = sa_end_transaction(
1646 						    impl_handle->scfhandle);
1647 					} else {
1648 						sa_abort_transaction(
1649 						    impl_handle->scfhandle);
1650 					}
1651 				}
1652 				if (ret != SA_OK) {
1653 					/*
1654 					 * Couldn't commit the group
1655 					 * so we need to undo
1656 					 * internally.
1657 					 */
1658 					xmlUnlinkNode(node);
1659 					xmlFreeNode(node);
1660 					node = NULL;
1661 				}
1662 			} else {
1663 				ret = SA_NO_MEMORY;
1664 			}
1665 		} else {
1666 			ret = SA_INVALID_NAME;
1667 		}
1668 	}
1669 err:
1670 	if (error != NULL)
1671 		*error = ret;
1672 	return ((sa_group_t)node);
1673 }
1674 
1675 /*
1676  * sa_remove_group(group)
1677  *
1678  * Remove the specified group. This deletes from the SMF repository.
1679  * All property groups and properties are removed.
1680  */
1681 
1682 int
1683 sa_remove_group(sa_group_t group)
1684 {
1685 	char *name;
1686 	int ret = SA_OK;
1687 	sa_handle_impl_t impl_handle;
1688 
1689 	impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1690 	if (impl_handle != NULL) {
1691 		name = sa_get_group_attr(group, "name");
1692 		if (name != NULL) {
1693 			ret = sa_delete_instance(impl_handle->scfhandle, name);
1694 			sa_free_attr_string(name);
1695 		}
1696 		xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */
1697 		xmlFreeNode((xmlNodePtr)group);   /* now it is gone */
1698 	} else {
1699 		ret = SA_SYSTEM_ERR;
1700 	}
1701 	return (ret);
1702 }
1703 
1704 /*
1705  * sa_update_config()
1706  *
1707  * Used to update legacy files that need to be updated in bulk
1708  * Currently, this is a placeholder and will go away in a future
1709  * release.
1710  */
1711 
1712 int
1713 sa_update_config(sa_handle_t handle)
1714 {
1715 	/*
1716 	 * do legacy files first so we can tell when they change.
1717 	 * This will go away when we start updating individual records
1718 	 * rather than the whole file.
1719 	 */
1720 	update_legacy_config(handle);
1721 	return (SA_OK);
1722 }
1723 
1724 /*
1725  * get_node_attr(node, tag)
1726  *
1727  * Get the speficied tag(attribute) if it exists on the node.  This is
1728  * used internally by a number of attribute oriented functions.
1729  */
1730 
1731 static char *
1732 get_node_attr(void *nodehdl, char *tag)
1733 {
1734 	xmlNodePtr node = (xmlNodePtr)nodehdl;
1735 	xmlChar *name = NULL;
1736 
1737 	if (node != NULL)
1738 		name = xmlGetProp(node, (xmlChar *)tag);
1739 	return ((char *)name);
1740 }
1741 
1742 /*
1743  * get_node_attr(node, tag)
1744  *
1745  * Set the speficied tag(attribute) to the specified value This is
1746  * used internally by a number of attribute oriented functions. It
1747  * doesn't update the repository, only the internal document state.
1748  */
1749 
1750 void
1751 set_node_attr(void *nodehdl, char *tag, char *value)
1752 {
1753 	xmlNodePtr node = (xmlNodePtr)nodehdl;
1754 	if (node != NULL && tag != NULL) {
1755 		if (value != NULL)
1756 			xmlSetProp(node, (xmlChar *)tag, (xmlChar *)value);
1757 		else
1758 			xmlUnsetProp(node, (xmlChar *)tag);
1759 	}
1760 }
1761 
1762 /*
1763  * sa_get_group_attr(group, tag)
1764  *
1765  * Get the specied attribute, if defined, for the group.
1766  */
1767 
1768 char *
1769 sa_get_group_attr(sa_group_t group, char *tag)
1770 {
1771 	return (get_node_attr((void *)group, tag));
1772 }
1773 
1774 /*
1775  * sa_set_group_attr(group, tag, value)
1776  *
1777  * set the specified tag/attribute on the group using value as its
1778  * value.
1779  *
1780  * This will result in setting the property in the SMF repository as
1781  * well as in the internal document.
1782  */
1783 
1784 int
1785 sa_set_group_attr(sa_group_t group, char *tag, char *value)
1786 {
1787 	int ret;
1788 	char *groupname;
1789 	sa_handle_impl_t impl_handle;
1790 
1791 	impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
1792 	if (impl_handle != NULL) {
1793 		groupname = sa_get_group_attr(group, "name");
1794 		ret = sa_get_instance(impl_handle->scfhandle, groupname);
1795 		if (ret == SA_OK) {
1796 			set_node_attr((void *)group, tag, value);
1797 			ret = sa_start_transaction(impl_handle->scfhandle,
1798 			    "operation");
1799 			if (ret == SA_OK) {
1800 				ret = sa_set_property(impl_handle->scfhandle,
1801 				    tag, value);
1802 				if (ret == SA_OK)
1803 					(void) sa_end_transaction(
1804 					    impl_handle->scfhandle);
1805 				else
1806 					sa_abort_transaction(
1807 					    impl_handle->scfhandle);
1808 			}
1809 		}
1810 		if (groupname != NULL)
1811 			sa_free_attr_string(groupname);
1812 	} else {
1813 		ret = SA_SYSTEM_ERR;
1814 	}
1815 	return (ret);
1816 }
1817 
1818 /*
1819  * sa_get_share_attr(share, tag)
1820  *
1821  * Return the value of the tag/attribute set on the specified
1822  * share. Returns NULL if the tag doesn't exist.
1823  */
1824 
1825 char *
1826 sa_get_share_attr(sa_share_t share, char *tag)
1827 {
1828 	return (get_node_attr((void *)share, tag));
1829 }
1830 
1831 /*
1832  * sa_get_resource(group, resource)
1833  *
1834  * Search all the shares in the speified group for a share with a
1835  * resource name matching the one specified.
1836  *
1837  * In the future, it may be advantageous to allow group to be NULL and
1838  * search all groups but that isn't needed at present.
1839  */
1840 
1841 sa_share_t
1842 sa_get_resource(sa_group_t group, char *resource)
1843 {
1844 	sa_share_t share = NULL;
1845 	char *name = NULL;
1846 
1847 	if (resource != NULL) {
1848 		for (share = sa_get_share(group, NULL); share != NULL;
1849 		    share = sa_get_next_share(share)) {
1850 			name = sa_get_share_attr(share, "resource");
1851 			if (name != NULL) {
1852 				if (strcmp(name, resource) == 0)
1853 					break;
1854 				sa_free_attr_string(name);
1855 				name = NULL;
1856 			}
1857 		}
1858 		if (name != NULL)
1859 			sa_free_attr_string(name);
1860 	}
1861 	return ((sa_share_t)share);
1862 }
1863 
1864 /*
1865  * _sa_set_share_description(share, description)
1866  *
1867  * Add a description tag with text contents to the specified share.
1868  * A separate XML tag is used rather than a property.
1869  */
1870 
1871 xmlNodePtr
1872 _sa_set_share_description(sa_share_t share, char *content)
1873 {
1874 	xmlNodePtr node;
1875 	node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description",
1876 	    NULL);
1877 	xmlNodeSetContent(node, (xmlChar *)content);
1878 	return (node);
1879 }
1880 
1881 /*
1882  * sa_set_share_attr(share, tag, value)
1883  *
1884  * Set the share attribute specified by tag to the specified value. In
1885  * the case of "resource", enforce a no duplicates in a group rule. If
1886  * the share is not transient, commit the changes to the repository
1887  * else just update the share internally.
1888  */
1889 
1890 int
1891 sa_set_share_attr(sa_share_t share, char *tag, char *value)
1892 {
1893 	sa_group_t group;
1894 	sa_share_t resource;
1895 	int ret = SA_OK;
1896 
1897 	group = sa_get_parent_group(share);
1898 
1899 	/*
1900 	 * There are some attributes that may have specific
1901 	 * restrictions on them. Initially, only "resource" has
1902 	 * special meaning that needs to be checked. Only one instance
1903 	 * of a resource name may exist within a group.
1904 	 */
1905 
1906 	if (strcmp(tag, "resource") == 0) {
1907 		resource = sa_get_resource(group, value);
1908 		if (resource != share && resource != NULL)
1909 			ret = SA_DUPLICATE_NAME;
1910 	}
1911 	if (ret == SA_OK) {
1912 		set_node_attr((void *)share, tag, value);
1913 		if (group != NULL) {
1914 			char *type;
1915 			/* we can probably optimize this some */
1916 			type = sa_get_share_attr(share, "type");
1917 			if (type == NULL || strcmp(type, "transient") != 0) {
1918 				sa_handle_impl_t impl_handle;
1919 				impl_handle =
1920 				    (sa_handle_impl_t)sa_find_group_handle(
1921 				    group);
1922 				if (impl_handle != NULL) {
1923 					ret = sa_commit_share(
1924 					    impl_handle->scfhandle, group,
1925 					    share);
1926 				} else {
1927 					ret = SA_SYSTEM_ERR;
1928 				}
1929 			}
1930 			if (type != NULL)
1931 				sa_free_attr_string(type);
1932 		}
1933 	}
1934 	return (ret);
1935 }
1936 
1937 /*
1938  * sa_get_property_attr(prop, tag)
1939  *
1940  * Get the value of the specified property attribute. Standard
1941  * attributes are "type" and "value".
1942  */
1943 
1944 char *
1945 sa_get_property_attr(sa_property_t prop, char *tag)
1946 {
1947 	return (get_node_attr((void *)prop, tag));
1948 }
1949 
1950 /*
1951  * sa_get_optionset_attr(prop, tag)
1952  *
1953  * Get the value of the specified property attribute. Standard
1954  * attribute is "type".
1955  */
1956 
1957 char *
1958 sa_get_optionset_attr(sa_property_t optionset, char *tag)
1959 {
1960 	return (get_node_attr((void *)optionset, tag));
1961 
1962 }
1963 
1964 /*
1965  * sa_set_optionset_attr(optionset, tag, value)
1966  *
1967  * Set the specified attribute(tag) to the specified value on the
1968  * optionset.
1969  */
1970 
1971 void
1972 sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value)
1973 {
1974 	set_node_attr((void *)optionset, tag, value);
1975 }
1976 
1977 /*
1978  * sa_free_attr_string(string)
1979  *
1980  * Free the string that was returned in one of the sa_get_*_attr()
1981  * functions.
1982  */
1983 
1984 void
1985 sa_free_attr_string(char *string)
1986 {
1987 	xmlFree((xmlChar *)string);
1988 }
1989 
1990 /*
1991  * sa_get_optionset(group, proto)
1992  *
1993  * Return the optionset, if it exists, that is associated with the
1994  * specified protocol.
1995  */
1996 
1997 sa_optionset_t
1998 sa_get_optionset(void *group, char *proto)
1999 {
2000 	xmlNodePtr node;
2001 	xmlChar *value = NULL;
2002 
2003 	for (node = ((xmlNodePtr)group)->children; node != NULL;
2004 	    node = node->next) {
2005 		if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2006 			value = xmlGetProp(node, (xmlChar *)"type");
2007 			if (proto != NULL) {
2008 				if (value != NULL &&
2009 				    xmlStrcmp(value, (xmlChar *)proto) == 0) {
2010 					break;
2011 				}
2012 				if (value != NULL) {
2013 					xmlFree(value);
2014 					value = NULL;
2015 				}
2016 			} else {
2017 				break;
2018 			}
2019 		}
2020 	}
2021 	if (value != NULL)
2022 		xmlFree(value);
2023 	return ((sa_optionset_t)node);
2024 }
2025 
2026 /*
2027  * sa_get_next_optionset(optionset)
2028  *
2029  * Return the next optionset in the group. NULL if this was the last.
2030  */
2031 
2032 sa_optionset_t
2033 sa_get_next_optionset(sa_optionset_t optionset)
2034 {
2035 	xmlNodePtr node;
2036 
2037 	for (node = ((xmlNodePtr)optionset)->next; node != NULL;
2038 	    node = node->next) {
2039 		if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) {
2040 			break;
2041 		}
2042 	}
2043 	return ((sa_optionset_t)node);
2044 }
2045 
2046 /*
2047  * sa_get_security(group, sectype, proto)
2048  *
2049  * Return the security optionset. The internal name is a hold over
2050  * from the implementation and will be changed before the API is
2051  * finalized. This is really a named optionset that can be negotiated
2052  * as a group of properties (like NFS security options).
2053  */
2054 
2055 sa_security_t
2056 sa_get_security(sa_group_t group, char *sectype, char *proto)
2057 {
2058 	xmlNodePtr node;
2059 	xmlChar *value = NULL;
2060 
2061 	for (node = ((xmlNodePtr)group)->children; node != NULL;
2062 	    node = node->next) {
2063 		if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2064 			if (proto != NULL) {
2065 				value = xmlGetProp(node, (xmlChar *)"type");
2066 				if (value == NULL ||
2067 				    (value != NULL &&
2068 				    xmlStrcmp(value, (xmlChar *)proto) != 0)) {
2069 					/* it doesn't match so continue */
2070 					xmlFree(value);
2071 					value = NULL;
2072 					continue;
2073 				}
2074 			}
2075 			if (value != NULL) {
2076 				xmlFree(value);
2077 				value = NULL;
2078 			}
2079 			/* potential match */
2080 			if (sectype != NULL) {
2081 				value = xmlGetProp(node, (xmlChar *)"sectype");
2082 				if (value != NULL &&
2083 				    xmlStrcmp(value, (xmlChar *)sectype) == 0) {
2084 					break;
2085 				}
2086 			} else {
2087 				break;
2088 			}
2089 		}
2090 		if (value != NULL) {
2091 			xmlFree(value);
2092 			value = NULL;
2093 		}
2094 	}
2095 	if (value != NULL)
2096 		xmlFree(value);
2097 	return ((sa_security_t)node);
2098 }
2099 
2100 /*
2101  * sa_get_next_security(security)
2102  *
2103  * Get the next security optionset if one exists.
2104  */
2105 
2106 sa_security_t
2107 sa_get_next_security(sa_security_t security)
2108 {
2109 	xmlNodePtr node;
2110 
2111 	for (node = ((xmlNodePtr)security)->next; node != NULL;
2112 	    node = node->next) {
2113 		if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) {
2114 			break;
2115 		}
2116 	}
2117 	return ((sa_security_t)node);
2118 }
2119 
2120 /*
2121  * sa_get_property(optionset, prop)
2122  *
2123  * Get the property object with the name specified in prop from the
2124  * optionset.
2125  */
2126 
2127 sa_property_t
2128 sa_get_property(sa_optionset_t optionset, char *prop)
2129 {
2130 	xmlNodePtr node = (xmlNodePtr)optionset;
2131 	xmlChar *value = NULL;
2132 
2133 	if (optionset == NULL)
2134 		return (NULL);
2135 
2136 	for (node = node->children; node != NULL;
2137 	    node = node->next) {
2138 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2139 			if (prop == NULL)
2140 				break;
2141 			value = xmlGetProp(node, (xmlChar *)"type");
2142 			if (value != NULL &&
2143 			    xmlStrcmp(value, (xmlChar *)prop) == 0) {
2144 				break;
2145 			}
2146 			if (value != NULL) {
2147 				xmlFree(value);
2148 				value = NULL;
2149 			}
2150 		}
2151 	}
2152 	if (value != NULL)
2153 		xmlFree(value);
2154 	if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
2155 		/*
2156 		 * avoid a non option node -- it is possible to be a
2157 		 * text node
2158 		 */
2159 		node = NULL;
2160 	}
2161 	return ((sa_property_t)node);
2162 }
2163 
2164 /*
2165  * sa_get_next_property(property)
2166  *
2167  * Get the next property following the specified property. NULL if
2168  * this was the last.
2169  */
2170 
2171 sa_property_t
2172 sa_get_next_property(sa_property_t property)
2173 {
2174 	xmlNodePtr node;
2175 
2176 	for (node = ((xmlNodePtr)property)->next; node != NULL;
2177 	    node = node->next) {
2178 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
2179 			break;
2180 		}
2181 	}
2182 	return ((sa_property_t)node);
2183 }
2184 
2185 /*
2186  * sa_set_share_description(share, content)
2187  *
2188  * Set the description of share to content.
2189  */
2190 
2191 int
2192 sa_set_share_description(sa_share_t share, char *content)
2193 {
2194 	xmlNodePtr node;
2195 	sa_group_t group;
2196 	int ret = SA_OK;
2197 
2198 	for (node = ((xmlNodePtr)share)->children; node != NULL;
2199 	    node = node->next) {
2200 		if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2201 			break;
2202 		}
2203 	}
2204 	group = sa_get_parent_group(share);
2205 	/* no existing description but want to add */
2206 	if (node == NULL && content != NULL) {
2207 		/* add a description */
2208 		node = _sa_set_share_description(share, content);
2209 	} else if (node != NULL && content != NULL) {
2210 		/* update a description */
2211 		xmlNodeSetContent(node, (xmlChar *)content);
2212 	} else if (node != NULL && content == NULL) {
2213 		/* remove an existing description */
2214 		xmlUnlinkNode(node);
2215 		xmlFreeNode(node);
2216 	}
2217 	if (group != NULL && is_persistent((sa_group_t)share)) {
2218 		sa_handle_impl_t impl_handle;
2219 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2220 		if (impl_handle != NULL) {
2221 			ret = sa_commit_share(impl_handle->scfhandle, group,
2222 			    share);
2223 		} else {
2224 			ret = SA_SYSTEM_ERR;
2225 		}
2226 	}
2227 	return (ret);
2228 }
2229 
2230 /*
2231  * fixproblemchars(string)
2232  *
2233  * don't want any newline or tab characters in the text since these
2234  * could break display of data and legacy file formats.
2235  */
2236 static void
2237 fixproblemchars(char *str)
2238 {
2239 	int c;
2240 	for (c = *str; c != '\0'; c = *++str) {
2241 		if (c == '\t' || c == '\n')
2242 			*str = ' ';
2243 		else if (c == '"')
2244 			*str = '\'';
2245 	}
2246 }
2247 
2248 /*
2249  * sa_get_share_description(share)
2250  *
2251  * Return the description text for the specified share if it
2252  * exists. NULL if no description exists.
2253  */
2254 
2255 char *
2256 sa_get_share_description(sa_share_t share)
2257 {
2258 	xmlChar *description = NULL;
2259 	xmlNodePtr node;
2260 
2261 	for (node = ((xmlNodePtr)share)->children; node != NULL;
2262 	    node = node->next) {
2263 		if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) {
2264 			break;
2265 		}
2266 	}
2267 	if (node != NULL) {
2268 		description = xmlNodeGetContent((xmlNodePtr)share);
2269 		fixproblemchars((char *)description);
2270 	}
2271 	return ((char *)description);
2272 }
2273 
2274 /*
2275  * sa_free(share_description(description)
2276  *
2277  * Free the description string.
2278  */
2279 
2280 void
2281 sa_free_share_description(char *description)
2282 {
2283 	xmlFree((xmlChar *)description);
2284 }
2285 
2286 /*
2287  * sa_create_optionset(group, proto)
2288  *
2289  * Create an optionset for the specified protocol in the specied
2290  * group. This is manifested as a property group within SMF.
2291  */
2292 
2293 sa_optionset_t
2294 sa_create_optionset(sa_group_t group, char *proto)
2295 {
2296 	sa_optionset_t optionset;
2297 	sa_group_t parent = group;
2298 
2299 	optionset = sa_get_optionset(group, proto);
2300 	if (optionset != NULL) {
2301 		/* can't have a duplicate protocol */
2302 		optionset = NULL;
2303 	} else {
2304 		optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group,
2305 		    NULL, (xmlChar *)"optionset", NULL);
2306 		/*
2307 		 * only put to repository if on a group and we were
2308 		 * able to create an optionset.
2309 		 */
2310 		if (optionset != NULL) {
2311 			char oname[SA_STRSIZE];
2312 			char *groupname;
2313 			char *id = NULL;
2314 
2315 			if (sa_is_share(group))
2316 				parent = sa_get_parent_group((sa_share_t)group);
2317 
2318 			sa_set_optionset_attr(optionset, "type", proto);
2319 
2320 			if (sa_is_share(group)) {
2321 				id = sa_get_share_attr((sa_share_t)group, "id");
2322 			}
2323 			(void) sa_optionset_name(optionset, oname,
2324 			    sizeof (oname), id);
2325 			groupname = sa_get_group_attr(parent, "name");
2326 			if (groupname != NULL && is_persistent(group)) {
2327 				sa_handle_impl_t impl_handle;
2328 				impl_handle = (sa_handle_impl_t)
2329 				    sa_find_group_handle(group);
2330 				assert(impl_handle != NULL);
2331 				if (impl_handle != NULL) {
2332 					(void) sa_get_instance(
2333 					    impl_handle->scfhandle,
2334 					    groupname);
2335 					(void) sa_create_pgroup(
2336 					    impl_handle->scfhandle, oname);
2337 				}
2338 			}
2339 			if (groupname != NULL)
2340 				sa_free_attr_string(groupname);
2341 			if (id != NULL)
2342 				sa_free_attr_string(id);
2343 		}
2344 	}
2345 	return (optionset);
2346 }
2347 
2348 /*
2349  * sa_get_property_parent(property)
2350  *
2351  * Given a property, return the object it is a property of. This will
2352  * be an optionset of some type.
2353  */
2354 
2355 static sa_optionset_t
2356 sa_get_property_parent(sa_property_t property)
2357 {
2358 	xmlNodePtr node = NULL;
2359 
2360 	if (property != NULL)
2361 		node = ((xmlNodePtr)property)->parent;
2362 	return ((sa_optionset_t)node);
2363 }
2364 
2365 /*
2366  * sa_get_optionset_parent(optionset)
2367  *
2368  * Return the parent of the specified optionset. This could be a group
2369  * or a share.
2370  */
2371 
2372 static sa_group_t
2373 sa_get_optionset_parent(sa_optionset_t optionset)
2374 {
2375 	xmlNodePtr node = NULL;
2376 
2377 	if (optionset != NULL)
2378 		node = ((xmlNodePtr)optionset)->parent;
2379 	return ((sa_group_t)node);
2380 }
2381 
2382 /*
2383  * zfs_needs_update(share)
2384  *
2385  * In order to avoid making multiple updates to a ZFS share when
2386  * setting properties, the share attribute "changed" will be set to
2387  * true when a property is added or modifed.  When done adding
2388  * properties, we can then detect that an update is needed.  We then
2389  * clear the state here to detect additional changes.
2390  */
2391 
2392 static int
2393 zfs_needs_update(sa_share_t share)
2394 {
2395 	char *attr;
2396 	int result = 0;
2397 
2398 	attr = sa_get_share_attr(share, "changed");
2399 	if (attr != NULL) {
2400 		sa_free_attr_string(attr);
2401 		result = 1;
2402 	}
2403 	set_node_attr((void *)share, "changed", NULL);
2404 	return (result);
2405 }
2406 
2407 /*
2408  * zfs_set_update(share)
2409  *
2410  * Set the changed attribute of the share to true.
2411  */
2412 
2413 static void
2414 zfs_set_update(sa_share_t share)
2415 {
2416 	set_node_attr((void *)share, "changed", "true");
2417 }
2418 
2419 /*
2420  * sa_commit_properties(optionset, clear)
2421  *
2422  * Check if SMF or ZFS config and either update or abort the pending
2423  * changes.
2424  */
2425 
2426 int
2427 sa_commit_properties(sa_optionset_t optionset, int clear)
2428 {
2429 	sa_group_t group;
2430 	sa_group_t parent;
2431 	int zfs = 0;
2432 	int needsupdate = 0;
2433 	int ret = SA_OK;
2434 	sa_handle_impl_t impl_handle;
2435 
2436 	group = sa_get_optionset_parent(optionset);
2437 	if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) {
2438 		/* only update ZFS if on a share */
2439 		parent = sa_get_parent_group(group);
2440 		zfs++;
2441 		if (parent != NULL && is_zfs_group(parent))
2442 			needsupdate = zfs_needs_update(group);
2443 		else
2444 			zfs = 0;
2445 	}
2446 	if (zfs) {
2447 		if (!clear && needsupdate)
2448 			ret = sa_zfs_update((sa_share_t)group);
2449 	} else {
2450 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2451 		if (impl_handle != NULL) {
2452 			if (clear) {
2453 				(void) sa_abort_transaction(
2454 				    impl_handle->scfhandle);
2455 			} else {
2456 				ret = sa_end_transaction(
2457 				    impl_handle->scfhandle);
2458 			}
2459 		} else {
2460 			ret = SA_SYSTEM_ERR;
2461 		}
2462 	}
2463 	return (ret);
2464 }
2465 
2466 /*
2467  * sa_destroy_optionset(optionset)
2468  *
2469  * Remove the optionset from its group. Update the repostory to
2470  * reflect this change.
2471  */
2472 
2473 int
2474 sa_destroy_optionset(sa_optionset_t optionset)
2475 {
2476 	char name[SA_STRSIZE];
2477 	int len;
2478 	int ret;
2479 	char *id = NULL;
2480 	sa_group_t group;
2481 	int ispersist = 1;
2482 
2483 	/* now delete the prop group */
2484 	group = sa_get_optionset_parent(optionset);
2485 	if (group != NULL && sa_is_share(group)) {
2486 		ispersist = is_persistent(group);
2487 		id = sa_get_share_attr((sa_share_t)group, "id");
2488 	}
2489 	if (ispersist) {
2490 		sa_handle_impl_t impl_handle;
2491 		len = sa_optionset_name(optionset, name, sizeof (name), id);
2492 		impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2493 		if (impl_handle != NULL) {
2494 			if (len > 0) {
2495 				ret = sa_delete_pgroup(impl_handle->scfhandle,
2496 				    name);
2497 			}
2498 		} else {
2499 			ret = SA_SYSTEM_ERR;
2500 		}
2501 	}
2502 	xmlUnlinkNode((xmlNodePtr)optionset);
2503 	xmlFreeNode((xmlNodePtr)optionset);
2504 	if (id != NULL)
2505 		sa_free_attr_string(id);
2506 	return (ret);
2507 }
2508 
2509 /* private to the implementation */
2510 int
2511 _sa_remove_optionset(sa_optionset_t optionset)
2512 {
2513 	int ret = SA_OK;
2514 
2515 	xmlUnlinkNode((xmlNodePtr)optionset);
2516 	xmlFreeNode((xmlNodePtr)optionset);
2517 	return (ret);
2518 }
2519 
2520 /*
2521  * sa_create_security(group, sectype, proto)
2522  *
2523  * Create a security optionset (one that has a type name and a
2524  * proto). Security is left over from a pure NFS implementation. The
2525  * naming will change in the future when the API is released.
2526  */
2527 sa_security_t
2528 sa_create_security(sa_group_t group, char *sectype, char *proto)
2529 {
2530 	sa_security_t security;
2531 	char *id = NULL;
2532 	sa_group_t parent;
2533 	char *groupname = NULL;
2534 
2535 	if (group != NULL && sa_is_share(group)) {
2536 		id = sa_get_share_attr((sa_share_t)group, "id");
2537 		parent = sa_get_parent_group(group);
2538 		if (parent != NULL)
2539 			groupname = sa_get_group_attr(parent, "name");
2540 	} else if (group != NULL) {
2541 		groupname = sa_get_group_attr(group, "name");
2542 	}
2543 
2544 	security = sa_get_security(group, sectype, proto);
2545 	if (security != NULL) {
2546 		/* can't have a duplicate security option */
2547 		security = NULL;
2548 	} else {
2549 		security = (sa_security_t)xmlNewChild((xmlNodePtr)group,
2550 		    NULL, (xmlChar *)"security", NULL);
2551 		if (security != NULL) {
2552 			char oname[SA_STRSIZE];
2553 			sa_set_security_attr(security, "type", proto);
2554 
2555 			sa_set_security_attr(security, "sectype", sectype);
2556 			(void) sa_security_name(security, oname,
2557 			    sizeof (oname), id);
2558 			if (groupname != NULL && is_persistent(group)) {
2559 				sa_handle_impl_t impl_handle;
2560 				impl_handle =
2561 				    (sa_handle_impl_t)sa_find_group_handle(
2562 				    group);
2563 				if (impl_handle != NULL) {
2564 					(void) sa_get_instance(
2565 					    impl_handle->scfhandle, groupname);
2566 					(void) sa_create_pgroup(
2567 					    impl_handle->scfhandle, oname);
2568 				}
2569 			}
2570 		}
2571 	}
2572 	if (groupname != NULL)
2573 		sa_free_attr_string(groupname);
2574 	return (security);
2575 }
2576 
2577 /*
2578  * sa_destroy_security(security)
2579  *
2580  * Remove the specified optionset from the document and the
2581  * configuration.
2582  */
2583 
2584 int
2585 sa_destroy_security(sa_security_t security)
2586 {
2587 	char name[SA_STRSIZE];
2588 	int len;
2589 	int ret = SA_OK;
2590 	char *id = NULL;
2591 	sa_group_t group;
2592 	int iszfs = 0;
2593 	int ispersist = 1;
2594 
2595 	group = sa_get_optionset_parent(security);
2596 
2597 	if (group != NULL)
2598 		iszfs = sa_group_is_zfs(group);
2599 
2600 	if (group != NULL && !iszfs) {
2601 		if (sa_is_share(group))
2602 			ispersist = is_persistent(group);
2603 		id = sa_get_share_attr((sa_share_t)group, "id");
2604 	}
2605 	if (ispersist) {
2606 		len = sa_security_name(security, name, sizeof (name), id);
2607 		if (!iszfs && len > 0) {
2608 			sa_handle_impl_t impl_handle;
2609 			impl_handle =
2610 			    (sa_handle_impl_t)sa_find_group_handle(group);
2611 			if (impl_handle != NULL) {
2612 				ret = sa_delete_pgroup(impl_handle->scfhandle,
2613 				    name);
2614 			} else {
2615 				ret = SA_SYSTEM_ERR;
2616 			}
2617 		}
2618 	}
2619 	xmlUnlinkNode((xmlNodePtr)security);
2620 	xmlFreeNode((xmlNodePtr)security);
2621 	if (iszfs)
2622 		ret = sa_zfs_update(group);
2623 	if (id != NULL)
2624 		sa_free_attr_string(id);
2625 	return (ret);
2626 }
2627 
2628 /*
2629  * sa_get_security_attr(optionset, tag)
2630  *
2631  * Return the specified attribute value from the optionset.
2632  */
2633 
2634 char *
2635 sa_get_security_attr(sa_property_t optionset, char *tag)
2636 {
2637 	return (get_node_attr((void *)optionset, tag));
2638 
2639 }
2640 
2641 /*
2642  * sa_set_security_attr(optionset, tag, value)
2643  *
2644  * Set the optioset attribute specied by tag to the specified value.
2645  */
2646 
2647 void
2648 sa_set_security_attr(sa_group_t optionset, char *tag, char *value)
2649 {
2650 	set_node_attr((void *)optionset, tag, value);
2651 }
2652 
2653 /*
2654  * is_nodetype(node, type)
2655  *
2656  * Check to see if node is of the type specified.
2657  */
2658 
2659 static int
2660 is_nodetype(void *node, char *type)
2661 {
2662 	return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0);
2663 }
2664 
2665 
2666 /*
2667  * add_or_update()
2668  *
2669  * Add or update a property. Pulled out of sa_set_prop_by_prop for
2670  * readability.
2671  */
2672 static int
2673 add_or_update(scfutilhandle_t *scf_handle, int type, scf_value_t *value,
2674     scf_transaction_entry_t *entry, char *name, char *valstr)
2675 {
2676 	int ret = SA_SYSTEM_ERR;
2677 
2678 	if (value != NULL) {
2679 		if (type == SA_PROP_OP_ADD)
2680 			ret = scf_transaction_property_new(scf_handle->trans,
2681 			    entry, name, SCF_TYPE_ASTRING);
2682 		else
2683 			ret = scf_transaction_property_change(scf_handle->trans,
2684 			    entry, name, SCF_TYPE_ASTRING);
2685 		if (ret == 0) {
2686 			ret = scf_value_set_astring(value, valstr);
2687 			if (ret == 0)
2688 				ret = scf_entry_add_value(entry, value);
2689 			if (ret == 0)
2690 				return (ret);
2691 			scf_value_destroy(value);
2692 		} else {
2693 			scf_entry_destroy(entry);
2694 		}
2695 	}
2696 	return (SA_SYSTEM_ERR);
2697 }
2698 
2699 /*
2700  * sa_set_prop_by_prop(optionset, group, prop, type)
2701  *
2702  * Add/remove/update the specified property prop into the optionset or
2703  * share. If a share, sort out which property group based on GUID. In
2704  * all cases, the appropriate transaction is set (or ZFS share is
2705  * marked as needing an update)
2706  */
2707 
2708 static int
2709 sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group,
2710 			sa_property_t prop, int type)
2711 {
2712 	char *name;
2713 	char *valstr;
2714 	int ret = SA_OK;
2715 	scf_transaction_entry_t *entry;
2716 	scf_value_t *value;
2717 	int opttype; /* 1 == optionset, 0 == security */
2718 	char *id = NULL;
2719 	int iszfs = 0;
2720 	int isshare = 0;
2721 	sa_group_t parent = NULL;
2722 	sa_handle_impl_t impl_handle;
2723 	scfutilhandle_t  *scf_handle;
2724 
2725 	if (!is_persistent(group)) {
2726 		/*
2727 		 * if the group/share is not persistent we don't need
2728 		 * to do anything here
2729 		 */
2730 		return (SA_OK);
2731 	}
2732 	impl_handle = (sa_handle_impl_t)sa_find_group_handle(group);
2733 	if (impl_handle == NULL || impl_handle->scfhandle == NULL)
2734 		return (SA_SYSTEM_ERR);
2735 	scf_handle = impl_handle->scfhandle;
2736 	name = sa_get_property_attr(prop, "type");
2737 	valstr = sa_get_property_attr(prop, "value");
2738 	entry = scf_entry_create(scf_handle->handle);
2739 	opttype = is_nodetype((void *)optionset, "optionset");
2740 
2741 	if (valstr != NULL && entry != NULL) {
2742 		if (sa_is_share(group)) {
2743 			isshare = 1;
2744 			parent = sa_get_parent_group(group);
2745 			if (parent != NULL)
2746 				iszfs = is_zfs_group(parent);
2747 		} else {
2748 			iszfs = is_zfs_group(group);
2749 		}
2750 		if (!iszfs) {
2751 			if (scf_handle->trans == NULL) {
2752 				char oname[SA_STRSIZE];
2753 				char *groupname = NULL;
2754 				if (isshare) {
2755 					if (parent != NULL) {
2756 						groupname =
2757 						    sa_get_group_attr(parent,
2758 						    "name");
2759 					}
2760 					id =
2761 					    sa_get_share_attr((sa_share_t)group,
2762 					    "id");
2763 				} else {
2764 					groupname = sa_get_group_attr(group,
2765 					    "name");
2766 				}
2767 				if (groupname != NULL) {
2768 					ret = sa_get_instance(scf_handle,
2769 					    groupname);
2770 					sa_free_attr_string(groupname);
2771 				}
2772 				if (opttype)
2773 					(void) sa_optionset_name(optionset,
2774 					    oname, sizeof (oname), id);
2775 				else
2776 					(void) sa_security_name(optionset,
2777 					    oname, sizeof (oname), id);
2778 				ret = sa_start_transaction(scf_handle, oname);
2779 			}
2780 			if (ret == SA_OK) {
2781 				switch (type) {
2782 				case SA_PROP_OP_REMOVE:
2783 					ret = scf_transaction_property_delete(
2784 					    scf_handle->trans, entry, name);
2785 					break;
2786 				case SA_PROP_OP_ADD:
2787 				case SA_PROP_OP_UPDATE:
2788 					value = scf_value_create(
2789 					    scf_handle->handle);
2790 					ret = add_or_update(scf_handle, type,
2791 					    value, entry, name, valstr);
2792 					break;
2793 				}
2794 			}
2795 		} else {
2796 			/*
2797 			 * ZFS update. The calling function would have updated
2798 			 * the internal XML structure. Just need to flag it as
2799 			 * changed for ZFS.
2800 			 */
2801 			zfs_set_update((sa_share_t)group);
2802 		}
2803 	}
2804 
2805 	if (name != NULL)
2806 		sa_free_attr_string(name);
2807 	if (valstr != NULL)
2808 		sa_free_attr_string(valstr);
2809 	else if (entry != NULL)
2810 		scf_entry_destroy(entry);
2811 
2812 	if (ret == -1)
2813 		ret = SA_SYSTEM_ERR;
2814 
2815 	return (ret);
2816 }
2817 
2818 /*
2819  * sa_create_property(name, value)
2820  *
2821  * Create a new property with the specified name and value.
2822  */
2823 
2824 sa_property_t
2825 sa_create_property(char *name, char *value)
2826 {
2827 	xmlNodePtr node;
2828 
2829 	node = xmlNewNode(NULL, (xmlChar *)"option");
2830 	if (node != NULL) {
2831 		xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name);
2832 		xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value);
2833 	}
2834 	return ((sa_property_t)node);
2835 }
2836 
2837 /*
2838  * sa_add_property(object, property)
2839  *
2840  * Add the specified property to the object. Issue the appropriate
2841  * transaction or mark a ZFS object as needing an update.
2842  */
2843 
2844 int
2845 sa_add_property(void *object, sa_property_t property)
2846 {
2847 	int ret = SA_OK;
2848 	sa_group_t parent;
2849 	sa_group_t group;
2850 	char *proto;
2851 
2852 	proto = sa_get_optionset_attr(object, "type");
2853 	if (property != NULL) {
2854 		if ((ret = sa_valid_property(object, proto, property)) ==
2855 		    SA_OK) {
2856 			property = (sa_property_t)xmlAddChild(
2857 			    (xmlNodePtr)object, (xmlNodePtr)property);
2858 		} else {
2859 			if (proto != NULL)
2860 				sa_free_attr_string(proto);
2861 			return (ret);
2862 		}
2863 	}
2864 
2865 	if (proto != NULL)
2866 		sa_free_attr_string(proto);
2867 
2868 	parent = sa_get_parent_group(object);
2869 	if (!is_persistent(parent)) {
2870 		return (ret);
2871 	}
2872 
2873 	if (sa_is_share(parent))
2874 		group = sa_get_parent_group(parent);
2875 	else
2876 		group = parent;
2877 
2878 	if (property == NULL) {
2879 		ret = SA_NO_MEMORY;
2880 	} else {
2881 		char oname[SA_STRSIZE];
2882 
2883 		if (!is_zfs_group(group)) {
2884 			char *id = NULL;
2885 			sa_handle_impl_t impl_handle;
2886 			scfutilhandle_t  *scf_handle;
2887 
2888 			impl_handle = (sa_handle_impl_t)sa_find_group_handle(
2889 			    group);
2890 			if (impl_handle == NULL ||
2891 			    impl_handle->scfhandle == NULL)
2892 				ret = SA_SYSTEM_ERR;
2893 			if (ret == SA_OK) {
2894 				scf_handle = impl_handle->scfhandle;
2895 				if (sa_is_share((sa_group_t)parent)) {
2896 					id = sa_get_share_attr(
2897 					    (sa_share_t)parent, "id");
2898 				}
2899 				if (scf_handle->trans == NULL) {
2900 					if (is_nodetype(object, "optionset")) {
2901 						(void) sa_optionset_name(
2902 						    (sa_optionset_t)object,
2903 						    oname, sizeof (oname), id);
2904 					} else {
2905 						(void) sa_security_name(
2906 						    (sa_optionset_t)object,
2907 						    oname, sizeof (oname), id);
2908 					}
2909 					ret = sa_start_transaction(scf_handle,
2910 					    oname);
2911 				}
2912 				if (ret == SA_OK) {
2913 					char *name;
2914 					char *value;
2915 					name = sa_get_property_attr(property,
2916 					    "type");
2917 					value = sa_get_property_attr(property,
2918 					    "value");
2919 					if (name != NULL && value != NULL) {
2920 						if (scf_handle->scf_state ==
2921 						    SCH_STATE_INIT) {
2922 							ret = sa_set_property(
2923 							    scf_handle, name,
2924 							    value);
2925 						}
2926 					} else {
2927 						ret = SA_CONFIG_ERR;
2928 					}
2929 					if (name != NULL)
2930 						sa_free_attr_string(
2931 						    name);
2932 					if (value != NULL)
2933 						sa_free_attr_string(value);
2934 				}
2935 				if (id != NULL)
2936 					sa_free_attr_string(id);
2937 			}
2938 		} else {
2939 			/*
2940 			 * ZFS is a special case. We do want
2941 			 * to allow editing property/security
2942 			 * lists since we can have a better
2943 			 * syntax and we also want to keep
2944 			 * things consistent when possible.
2945 			 *
2946 			 * Right now, we defer until the
2947 			 * sa_commit_properties so we can get
2948 			 * them all at once. We do need to
2949 			 * mark the share as "changed"
2950 			 */
2951 			zfs_set_update((sa_share_t)parent);
2952 		}
2953 	}
2954 	return (ret);
2955 }
2956 
2957 /*
2958  * sa_remove_property(property)
2959  *
2960  * Remove the specied property from its containing object. Update the
2961  * repository as appropriate.
2962  */
2963 
2964 int
2965 sa_remove_property(sa_property_t property)
2966 {
2967 	int ret = SA_OK;
2968 
2969 	if (property != NULL) {
2970 		sa_optionset_t optionset;
2971 		sa_group_t group;
2972 		optionset = sa_get_property_parent(property);
2973 		if (optionset != NULL) {
2974 			group = sa_get_optionset_parent(optionset);
2975 			if (group != NULL) {
2976 				ret = sa_set_prop_by_prop(optionset, group,
2977 				    property, SA_PROP_OP_REMOVE);
2978 			}
2979 		}
2980 		xmlUnlinkNode((xmlNodePtr)property);
2981 		xmlFreeNode((xmlNodePtr)property);
2982 	} else {
2983 		ret = SA_NO_SUCH_PROP;
2984 	}
2985 	return (ret);
2986 }
2987 
2988 /*
2989  * sa_update_property(property, value)
2990  *
2991  * Update the specified property to the new value.  If value is NULL,
2992  * we currently treat this as a remove.
2993  */
2994 
2995 int
2996 sa_update_property(sa_property_t property, char *value)
2997 {
2998 	int ret = SA_OK;
2999 	if (value == NULL) {
3000 		return (sa_remove_property(property));
3001 	} else {
3002 		sa_optionset_t optionset;
3003 		sa_group_t group;
3004 		set_node_attr((void *)property, "value", value);
3005 		optionset = sa_get_property_parent(property);
3006 		if (optionset != NULL) {
3007 			group = sa_get_optionset_parent(optionset);
3008 			if (group != NULL) {
3009 				ret = sa_set_prop_by_prop(optionset, group,
3010 				    property, SA_PROP_OP_UPDATE);
3011 			}
3012 		} else {
3013 			ret = SA_NO_SUCH_PROP;
3014 		}
3015 	}
3016 	return (ret);
3017 }
3018 
3019 /*
3020  * sa_get_protocol_property(propset, prop)
3021  *
3022  * Get the specified protocol specific property. These are global to
3023  * the protocol and not specific to a group or share.
3024  */
3025 
3026 sa_property_t
3027 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop)
3028 {
3029 	xmlNodePtr node = (xmlNodePtr)propset;
3030 	xmlChar *value = NULL;
3031 
3032 	for (node = node->children; node != NULL;
3033 	    node = node->next) {
3034 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3035 			if (prop == NULL)
3036 				break;
3037 			value = xmlGetProp(node, (xmlChar *)"type");
3038 			if (value != NULL &&
3039 			    xmlStrcasecmp(value, (xmlChar *)prop) == 0) {
3040 				break;
3041 			}
3042 			if (value != NULL) {
3043 				xmlFree(value);
3044 				value = NULL;
3045 			}
3046 		}
3047 	}
3048 	if (value != NULL)
3049 		xmlFree(value);
3050 	if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) {
3051 		/*
3052 		 * avoid a non option node -- it is possible to be a
3053 		 * text node
3054 		 */
3055 		node = NULL;
3056 	}
3057 	return ((sa_property_t)node);
3058 }
3059 
3060 /*
3061  * sa_get_next_protocol_property(prop)
3062  *
3063  * Get the next protocol specific property in the list.
3064  */
3065 
3066 sa_property_t
3067 sa_get_next_protocol_property(sa_property_t prop)
3068 {
3069 	xmlNodePtr node;
3070 
3071 	for (node = ((xmlNodePtr)prop)->next; node != NULL;
3072 	    node = node->next) {
3073 		if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) {
3074 			break;
3075 		}
3076 	}
3077 	return ((sa_property_t)node);
3078 }
3079 
3080 /*
3081  * sa_set_protocol_property(prop, value)
3082  *
3083  * Set the specified property to have the new value.  The protocol
3084  * specific plugin will then be called to update the property.
3085  */
3086 
3087 int
3088 sa_set_protocol_property(sa_property_t prop, char *value)
3089 {
3090 	sa_protocol_properties_t propset;
3091 	char *proto;
3092 	int ret = SA_INVALID_PROTOCOL;
3093 
3094 	propset = ((xmlNodePtr)prop)->parent;
3095 	if (propset != NULL) {
3096 		proto = sa_get_optionset_attr(propset, "type");
3097 		if (proto != NULL) {
3098 			set_node_attr((xmlNodePtr)prop, "value", value);
3099 			ret = sa_proto_set_property(proto, prop);
3100 			sa_free_attr_string(proto);
3101 		}
3102 	}
3103 	return (ret);
3104 }
3105 
3106 /*
3107  * sa_add_protocol_property(propset, prop)
3108  *
3109  * Add a new property to the protocol sepcific property set.
3110  */
3111 
3112 int
3113 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop)
3114 {
3115 	xmlNodePtr node;
3116 
3117 	/* should check for legitimacy */
3118 	node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop);
3119 	if (node != NULL)
3120 		return (SA_OK);
3121 	return (SA_NO_MEMORY);
3122 }
3123 
3124 /*
3125  * sa_create_protocol_properties(proto)
3126  *
3127  * Create a protocol specifity property set.
3128  */
3129 
3130 sa_protocol_properties_t
3131 sa_create_protocol_properties(char *proto)
3132 {
3133 	xmlNodePtr node;
3134 
3135 	node = xmlNewNode(NULL, (xmlChar *)"propertyset");
3136 	if (node != NULL)
3137 		xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto);
3138 	return (node);
3139 }
3140