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