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